This commit is contained in:
daya 2020-09-04 10:58:54 +08:00
parent 0be766e71a
commit 44a8bcb152
29 changed files with 1645 additions and 0 deletions

22
debian/.gitignore vendored Normal file
View file

@ -0,0 +1,22 @@
# Ignore generated files
*.deb
*.dsc
*.changes
*.build
*.buildinfo
*.tar.gz
# Ignore files generated during build
files
autoreconf.*
*.substvars
*.log
*.debhelper
*-stamp
/*/
!missing-sources/
!patches/
!patches/*.patch
!source/
!tests/
!upstream/

28
debian/changelog vendored Normal file
View file

@ -0,0 +1,28 @@
scrcpy (1.16-1) unstable; urgency=medium
* build 1.16.
*
-- zhaozhen <zhaozhen@uniontech.com> Wed, 02 Sep 2020 18:23:17 +0800
scrcpy (1.14-1) unstable; urgency=medium
* New upstream release
* Bump Standards-Version to 4.5.0
* Bump debhelper compat to 13
-- Yangfl <mmyangfl@gmail.com> Wed, 01 Jul 2020 14:59:53 +0800
scrcpy (1.12.1+ds-1) unstable; urgency=medium
* New upstream release (Closes: #947465)
* Recommend but not depend on adb
* Add upstream metadata
-- Yangfl <mmyangfl@gmail.com> Tue, 14 Jan 2020 13:14:59 +0800
scrcpy (1.11+ds-1) unstable; urgency=medium
* Initial release (Closes: #893279)
-- Yangfl <mmyangfl@gmail.com> Wed, 20 Nov 2019 23:40:21 +0800

44
debian/control vendored Normal file
View file

@ -0,0 +1,44 @@
Source: scrcpy
Section: net
Priority: optional
Maintainer: Yangfl <mmyangfl@gmail.com>
Build-Depends:
debhelper-compat (= 13),
Build-Depends-Arch:
meson,
libavcodec-dev,
libavformat-dev,
libavutil-dev,
libsdl2-dev,
Build-Depends-Indep:
android-sdk,
android-sdk-platform-23,
default-jdk,
unzip,
zip,
Rules-Requires-Root: no
Standards-Version: 4.5.0
Homepage: https://github.com/Genymobile/scrcpy
Vcs-Git: https://salsa.debian.org/yangfl-guest/scrcpy.git
Vcs-Browser: https://salsa.debian.org/yangfl-guest/scrcpy
Package: scrcpy
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}, scrcpy-server,
Recommends: adb,
Description: Display and control your Android device
This application provides display and control of Android devices connected on
USB (or over TCP/IP). It does not require any root access.
.
This package contains the client (desktop) binary.
Package: scrcpy-server
Architecture: all
Multi-Arch: foreign
Depends: ${misc:Depends},
Description: Display and control your Android device - server binary
This application provides display and control of Android devices connected on
USB (or over TCP/IP). It does not require any root access.
.
This package contains the server (mobile) binary, which will be pushed to the
Android device.

38
debian/copyright vendored Normal file
View file

@ -0,0 +1,38 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: scrcpy
Upstream-Contact: Romain Vimont <rom@rom1v.com>
Source: https://github.com/Genymobile/scrcpy
Files: *
Copyright: 2018 Genymobile
2018-2019 Romain Vimont <rom@rom1v.com>
License: Apache-2
Files: app/src/android/input.h
app/src/android/keycodes.h
Copyright: 2010 The Android Open Source Project
License: Apache-2
Files: debian/missing-sources/gradle/wrapper/gradle-wrapper/*
Copyright: 2007-2017 Gradle authors
License: Apache-2
Files: debian/*
Copyright: 2019 Yangfl <mmyangfl@gmail.com>
License: Apache-2
License: Apache-2
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
.
On Debian systems, the full text of the Apache License, Version 2.0
can be found in the file '/usr/share/common-licenses/Apache-2.0'.

4
debian/gbp.conf vendored Normal file
View file

@ -0,0 +1,4 @@
[DEFAULT]
upstream-branch = upstream
debian-branch = master
pristine-tar = True

3
debian/gitlab-ci.yml vendored Normal file
View file

@ -0,0 +1,3 @@
include:
- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml

View file

@ -0,0 +1,56 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
import java.io.Closeable;
import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class BootstrapMainStarter {
public void start(String[] args, File gradleHome) throws Exception {
File gradleJar = findLauncherJar(gradleHome);
if (gradleJar == null) {
throw new RuntimeException(String.format("Could not locate the Gradle launcher JAR in Gradle distribution '%s'.", gradleHome));
}
URLClassLoader contextClassLoader = new URLClassLoader(new URL[]{gradleJar.toURI().toURL()}, ClassLoader.getSystemClassLoader().getParent());
Thread.currentThread().setContextClassLoader(contextClassLoader);
Class<?> mainClass = contextClassLoader.loadClass("org.gradle.launcher.GradleMain");
Method mainMethod = mainClass.getMethod("main", String[].class);
mainMethod.invoke(null, new Object[]{args});
if (contextClassLoader instanceof Closeable) {
((Closeable) contextClassLoader).close();
}
}
static File findLauncherJar(File gradleHome) {
File libDirectory = new File(gradleHome, "lib");
if (libDirectory.exists() && libDirectory.isDirectory()) {
File[] launcherJars = libDirectory.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.matches("gradle-launcher-.*\\.jar");
}
});
if (launcherJars!=null && launcherJars.length==1) {
return launcherJars[0];
}
}
return null;
}
}

View file

@ -0,0 +1,235 @@
/*
* Copyright 2007-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
public class Download implements IDownload {
public static final String UNKNOWN_VERSION = "0";
private static final int BUFFER_SIZE = 10 * 1024;
private static final int PROGRESS_CHUNK = 1024 * 1024;
private final Logger logger;
private final String appName;
private final String appVersion;
private final DownloadProgressListener progressListener;
public Download(Logger logger, String appName, String appVersion) {
this(logger, null, appName, appVersion);
}
public Download(Logger logger, DownloadProgressListener progressListener, String appName, String appVersion) {
this.logger = logger;
this.appName = appName;
this.appVersion = appVersion;
this.progressListener = new DefaultDownloadProgressListener(logger, progressListener);
configureProxyAuthentication();
}
private void configureProxyAuthentication() {
if (System.getProperty("http.proxyUser") != null) {
Authenticator.setDefault(new ProxyAuthenticator());
}
}
public void download(URI address, File destination) throws Exception {
destination.getParentFile().mkdirs();
downloadInternal(address, destination);
}
private void downloadInternal(URI address, File destination)
throws Exception {
OutputStream out = null;
URLConnection conn;
InputStream in = null;
try {
URL url = safeUri(address).toURL();
out = new BufferedOutputStream(new FileOutputStream(destination));
conn = url.openConnection();
addBasicAuthentication(address, conn);
final String userAgentValue = calculateUserAgent();
conn.setRequestProperty("User-Agent", userAgentValue);
in = conn.getInputStream();
byte[] buffer = new byte[BUFFER_SIZE];
int numRead;
int totalLength = conn.getContentLength();
long downloadedLength = 0;
long progressCounter = 0;
while ((numRead = in.read(buffer)) != -1) {
if (Thread.currentThread().isInterrupted()) {
System.out.print("interrupted");
throw new IOException("Download was interrupted.");
}
downloadedLength += numRead;
progressCounter += numRead;
if (progressCounter / PROGRESS_CHUNK > 0 || downloadedLength == totalLength) {
progressCounter = progressCounter - PROGRESS_CHUNK;
progressListener.downloadStatusChanged(address, totalLength, downloadedLength);
}
out.write(buffer, 0, numRead);
}
} finally {
logger.log("");
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
/**
* Create a safe URI from the given one by stripping out user info.
*
* @param uri Original URI
* @return a new URI with no user info
*/
static URI safeUri(URI uri) {
try {
return new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment());
} catch (URISyntaxException e) {
throw new RuntimeException("Failed to parse URI", e);
}
}
private void addBasicAuthentication(URI address, URLConnection connection) throws IOException {
String userInfo = calculateUserInfo(address);
if (userInfo == null) {
return;
}
if (!"https".equals(address.getScheme())) {
logger.log("WARNING Using HTTP Basic Authentication over an insecure connection to download the Gradle distribution. Please consider using HTTPS.");
}
connection.setRequestProperty("Authorization", "Basic " + base64Encode(userInfo));
}
/**
* Base64 encode user info for HTTP Basic Authentication.
*
* Try to use {@literal java.util.Base64} encoder which is available starting with Java 8.
* Fallback to {@literal javax.xml.bind.DatatypeConverter} from JAXB which is available starting with Java 6 but is not anymore in Java 9.
* Fortunately, both of these two Base64 encoders implement the right Base64 flavor, the one that does not split the output in multiple lines.
*
* @param userInfo user info
* @return Base64 encoded user info
* @throws RuntimeException if no public Base64 encoder is available on this JVM
*/
private String base64Encode(String userInfo) {
ClassLoader loader = getClass().getClassLoader();
try {
Method getEncoderMethod = loader.loadClass("java.util.Base64").getMethod("getEncoder");
Method encodeMethod = loader.loadClass("java.util.Base64$Encoder").getMethod("encodeToString", byte[].class);
Object encoder = getEncoderMethod.invoke(null);
return (String) encodeMethod.invoke(encoder, new Object[]{userInfo.getBytes("UTF-8")});
} catch (Exception java7OrEarlier) {
try {
Method encodeMethod = loader.loadClass("javax.xml.bind.DatatypeConverter").getMethod("printBase64Binary", byte[].class);
return (String) encodeMethod.invoke(null, new Object[]{userInfo.getBytes("UTF-8")});
} catch (Exception java5OrEarlier) {
throw new RuntimeException("Downloading Gradle distributions with HTTP Basic Authentication is not supported on your JVM.", java5OrEarlier);
}
}
}
private String calculateUserInfo(URI uri) {
String username = System.getProperty("gradle.wrapperUser");
String password = System.getProperty("gradle.wrapperPassword");
if (username != null && password != null) {
return username + ':' + password;
}
return uri.getUserInfo();
}
private String calculateUserAgent() {
String javaVendor = System.getProperty("java.vendor");
String javaVersion = System.getProperty("java.version");
String javaVendorVersion = System.getProperty("java.vm.version");
String osName = System.getProperty("os.name");
String osVersion = System.getProperty("os.version");
String osArch = System.getProperty("os.arch");
return String.format("%s/%s (%s;%s;%s) (%s;%s;%s)", appName, appVersion, osName, osVersion, osArch, javaVendor, javaVersion, javaVendorVersion);
}
private static class ProxyAuthenticator extends Authenticator {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
System.getProperty("http.proxyUser"), System.getProperty(
"http.proxyPassword", "").toCharArray());
}
}
private static class DefaultDownloadProgressListener implements DownloadProgressListener {
private final Logger logger;
private final DownloadProgressListener delegate;
private int previousDownloadPercent;
public DefaultDownloadProgressListener(Logger logger, DownloadProgressListener delegate) {
this.logger = logger;
this.delegate = delegate;
this.previousDownloadPercent = 0;
}
@Override
public void downloadStatusChanged(URI address, long contentLength, long downloaded) {
// If the total size of distribution is known, but there's no advanced progress listener, provide extra progress information
if (contentLength > 0 && delegate == null) {
appendPercentageSoFar(contentLength, downloaded);
}
if (contentLength != downloaded) {
logger.append(".");
}
if (delegate != null) {
delegate.downloadStatusChanged(address, contentLength, downloaded);
}
}
private void appendPercentageSoFar(long contentLength, long downloaded) {
try {
int currentDownloadPercent = 10 * (calculateDownloadPercent(contentLength, downloaded) / 10);
if (currentDownloadPercent != 0 && previousDownloadPercent != currentDownloadPercent) {
logger.append(String.valueOf(currentDownloadPercent)).append('%');
previousDownloadPercent = currentDownloadPercent;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private int calculateDownloadPercent(long totalLength, long downloadedLength) {
return Math.min(100, Math.max(0, (int) ((downloadedLength / (double) totalLength) * 100)));
}
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
import java.net.URI;
public interface DownloadProgressListener {
/**
* Reports the current progress of the download
*
* @param address distribution url
* @param contentLength the content length of the distribution, or -1 if the content length is not known.
* @param downloaded the total amount of currently downloaded bytes
*/
void downloadStatusChanged(URI address, long contentLength, long downloaded);
}

View file

@ -0,0 +1,97 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
import java.io.Closeable;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.concurrent.Callable;
public class ExclusiveFileAccessManager {
public static final String LOCK_FILE_SUFFIX = ".lck";
private final int timeoutMs;
private final int pollIntervalMs;
public ExclusiveFileAccessManager(int timeoutMs, int pollIntervalMs) {
this.timeoutMs = timeoutMs;
this.pollIntervalMs = pollIntervalMs;
}
public <T> T access(File exclusiveFile, Callable<T> task) throws Exception {
final File lockFile = new File(exclusiveFile.getParentFile(), exclusiveFile.getName() + LOCK_FILE_SUFFIX);
File lockFileDirectory = lockFile.getParentFile();
if (!lockFileDirectory.mkdirs()
&& (!lockFileDirectory.exists() || !lockFileDirectory.isDirectory())) {
throw new RuntimeException("Could not create parent directory for lock file " + lockFile.getAbsolutePath());
}
RandomAccessFile randomAccessFile = null;
FileChannel channel = null;
try {
long expiry = getTimeMillis() + timeoutMs;
FileLock lock = null;
while (lock == null && getTimeMillis() < expiry) {
randomAccessFile = new RandomAccessFile(lockFile, "rw");
channel = randomAccessFile.getChannel();
lock = channel.tryLock();
if (lock == null) {
maybeCloseQuietly(channel);
maybeCloseQuietly(randomAccessFile);
Thread.sleep(pollIntervalMs);
}
}
if (lock == null) {
throw new RuntimeException("Timeout of " + timeoutMs + " reached waiting for exclusive access to file: " + exclusiveFile.getAbsolutePath());
}
try {
return task.call();
} finally {
lock.release();
maybeCloseQuietly(channel);
channel = null;
maybeCloseQuietly(randomAccessFile);
randomAccessFile = null;
}
} finally {
maybeCloseQuietly(channel);
maybeCloseQuietly(randomAccessFile);
}
}
private long getTimeMillis() {
return System.nanoTime() / (1000L * 1000L);
}
private static void maybeCloseQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception ignore) {
//
}
}
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
import java.io.File;
public class GradleUserHomeLookup {
public static final String DEFAULT_GRADLE_USER_HOME = System.getProperty("user.home") + "/.gradle";
public static final String GRADLE_USER_HOME_PROPERTY_KEY = "gradle.user.home";
public static final String GRADLE_USER_HOME_ENV_KEY = "GRADLE_USER_HOME";
public static File gradleUserHome() {
String gradleUserHome;
if ((gradleUserHome = System.getProperty(GRADLE_USER_HOME_PROPERTY_KEY)) != null) {
return new File(gradleUserHome);
}
if ((gradleUserHome = System.getenv(GRADLE_USER_HOME_ENV_KEY)) != null) {
return new File(gradleUserHome);
}
return new File(DEFAULT_GRADLE_USER_HOME);
}
}

View file

@ -0,0 +1,109 @@
/*
* Copyright 2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
import org.gradle.cli.CommandLineParser;
import org.gradle.cli.ParsedCommandLine;
import org.gradle.cli.SystemPropertiesCommandLineConverter;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Properties;
import static org.gradle.wrapper.Download.UNKNOWN_VERSION;
public class GradleWrapperMain {
public static final String GRADLE_USER_HOME_OPTION = "g";
public static final String GRADLE_USER_HOME_DETAILED_OPTION = "gradle-user-home";
public static final String GRADLE_QUIET_OPTION = "q";
public static final String GRADLE_QUIET_DETAILED_OPTION = "quiet";
public static void main(String[] args) throws Exception {
File wrapperJar = wrapperJar();
File propertiesFile = wrapperProperties(wrapperJar);
File rootDir = rootDir(wrapperJar);
CommandLineParser parser = new CommandLineParser();
parser.allowUnknownOptions();
parser.option(GRADLE_USER_HOME_OPTION, GRADLE_USER_HOME_DETAILED_OPTION).hasArgument();
parser.option(GRADLE_QUIET_OPTION, GRADLE_QUIET_DETAILED_OPTION);
SystemPropertiesCommandLineConverter converter = new SystemPropertiesCommandLineConverter();
converter.configure(parser);
ParsedCommandLine options = parser.parse(args);
Properties systemProperties = System.getProperties();
systemProperties.putAll(converter.convert(options, new HashMap<String, String>()));
File gradleUserHome = gradleUserHome(options);
addSystemProperties(gradleUserHome, rootDir);
Logger logger = logger(options);
WrapperExecutor wrapperExecutor = WrapperExecutor.forWrapperPropertiesFile(propertiesFile);
wrapperExecutor.execute(
args,
new Install(logger, new Download(logger, "gradlew", UNKNOWN_VERSION), new PathAssembler(gradleUserHome)),
new BootstrapMainStarter());
}
private static void addSystemProperties(File gradleHome, File rootDir) {
System.getProperties().putAll(SystemPropertiesHandler.getSystemProperties(new File(gradleHome, "gradle.properties")));
System.getProperties().putAll(SystemPropertiesHandler.getSystemProperties(new File(rootDir, "gradle.properties")));
}
private static File rootDir(File wrapperJar) {
return wrapperJar.getParentFile().getParentFile().getParentFile();
}
private static File wrapperProperties(File wrapperJar) {
return new File(wrapperJar.getParent(), wrapperJar.getName().replaceFirst("\\.jar$", ".properties"));
}
private static File wrapperJar() {
URI location;
try {
location = GradleWrapperMain.class.getProtectionDomain().getCodeSource().getLocation().toURI();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
if (!location.getScheme().equals("file")) {
throw new RuntimeException(String.format("Cannot determine classpath for wrapper Jar from codebase '%s'.", location));
}
try {
return Paths.get(location).toFile();
} catch (NoClassDefFoundError e) {
return new File(location.getPath());
}
}
private static File gradleUserHome(ParsedCommandLine options) {
if (options.hasOption(GRADLE_USER_HOME_OPTION)) {
return new File(options.option(GRADLE_USER_HOME_OPTION).getValue());
}
return GradleUserHomeLookup.gradleUserHome();
}
private static Logger logger(ParsedCommandLine options) {
return new Logger(options.hasOption(GRADLE_QUIET_OPTION));
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright 2007-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
import java.io.File;
import java.net.URI;
public interface IDownload {
void download(URI address, File destination) throws Exception;
}

View file

@ -0,0 +1,297 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
import java.io.*;
import java.net.URI;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.security.MessageDigest;
public class Install {
public static final String DEFAULT_DISTRIBUTION_PATH = "wrapper/dists";
private final Logger logger;
private final IDownload download;
private final PathAssembler pathAssembler;
private final ExclusiveFileAccessManager exclusiveFileAccessManager = new ExclusiveFileAccessManager(120000, 200);
public Install(Logger logger, IDownload download, PathAssembler pathAssembler) {
this.logger = logger;
this.download = download;
this.pathAssembler = pathAssembler;
}
public File createDist(final WrapperConfiguration configuration) throws Exception {
final URI distributionUrl = configuration.getDistribution();
final String distributionSha256Sum = configuration.getDistributionSha256Sum();
final PathAssembler.LocalDistribution localDistribution = pathAssembler.getDistribution(configuration);
final File distDir = localDistribution.getDistributionDir();
final File localZipFile = localDistribution.getZipFile();
return exclusiveFileAccessManager.access(localZipFile, new Callable<File>() {
public File call() throws Exception {
final File markerFile = new File(localZipFile.getParentFile(), localZipFile.getName() + ".ok");
if (distDir.isDirectory() && markerFile.isFile()) {
InstallCheck installCheck = verifyDistributionRoot(distDir, distDir.getAbsolutePath());
if (installCheck.isVerified()) {
return installCheck.gradleHome;
}
// Distribution is invalid. Try to reinstall.
System.err.println(installCheck.failureMessage);
markerFile.delete();
}
boolean needsDownload = !localZipFile.isFile();
URI safeDistributionUrl = Download.safeUri(distributionUrl);
if (needsDownload) {
File tmpZipFile = new File(localZipFile.getParentFile(), localZipFile.getName() + ".part");
tmpZipFile.delete();
logger.log("Downloading " + safeDistributionUrl);
download.download(distributionUrl, tmpZipFile);
tmpZipFile.renameTo(localZipFile);
}
List<File> topLevelDirs = listDirs(distDir);
for (File dir : topLevelDirs) {
logger.log("Deleting directory " + dir.getAbsolutePath());
deleteDir(dir);
}
verifyDownloadChecksum(configuration.getDistribution().toString(), localZipFile, distributionSha256Sum);
try {
unzip(localZipFile, distDir);
} catch (IOException e) {
logger.log("Could not unzip " + localZipFile.getAbsolutePath() + " to " + distDir.getAbsolutePath() + ".");
logger.log("Reason: " + e.getMessage());
throw e;
}
InstallCheck installCheck = verifyDistributionRoot(distDir, safeDistributionUrl.toString());
if (installCheck.isVerified()) {
setExecutablePermissions(installCheck.gradleHome);
markerFile.createNewFile();
return installCheck.gradleHome;
}
// Distribution couldn't be installed.
throw new RuntimeException(installCheck.failureMessage);
}
});
}
private String calculateSha256Sum(File file)
throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
InputStream fis = new FileInputStream(file);
try {
int n = 0;
byte[] buffer = new byte[4096];
while (n != -1) {
n = fis.read(buffer);
if (n > 0) {
md.update(buffer, 0, n);
}
}
} finally {
fis.close();
}
byte byteData[] = md.digest();
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < byteData.length; i++) {
String hex = Integer.toHexString(0xff & byteData[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
private InstallCheck verifyDistributionRoot(File distDir, String distributionDescription)
throws Exception {
List<File> dirs = listDirs(distDir);
if (dirs.isEmpty()) {
return InstallCheck.failure(String.format("Gradle distribution '%s' does not contain any directories. Expected to find exactly 1 directory.", distributionDescription));
}
if (dirs.size() != 1) {
return InstallCheck.failure(String.format("Gradle distribution '%s' contains too many directories. Expected to find exactly 1 directory.", distributionDescription));
}
File gradleHome = dirs.get(0);
if (BootstrapMainStarter.findLauncherJar(gradleHome) == null) {
return InstallCheck.failure(String.format("Gradle distribution '%s' does not appear to contain a Gradle distribution.", distributionDescription));
}
return InstallCheck.success(gradleHome);
}
private void verifyDownloadChecksum(String sourceUrl, File localZipFile, String expectedSum) throws Exception {
if (expectedSum != null) {
// if a SHA-256 hash sum has been defined in gradle-wrapper.properties, verify it here
String actualSum = calculateSha256Sum(localZipFile);
if (!expectedSum.equals(actualSum)) {
localZipFile.delete();
String message = String.format("Verification of Gradle distribution failed!%n"
+ "%n"
+ "Your Gradle distribution may have been tampered with.%n"
+ "Confirm that the 'distributionSha256Sum' property in your gradle-wrapper.properties file is correct and you are downloading the wrapper from a trusted source.%n"
+ "%n"
+ " Distribution Url: %s%n"
+ "Download Location: %s%n"
+ "Expected checksum: '%s'%n"
+ " Actual checksum: '%s'%n",
sourceUrl, localZipFile.getAbsolutePath(), expectedSum, actualSum
);
throw new RuntimeException(message);
}
}
}
private List<File> listDirs(File distDir) {
List<File> dirs = new ArrayList<File>();
if (distDir.exists()) {
File[] files = distDir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
dirs.add(file);
}
}
}
}
return dirs;
}
private void setExecutablePermissions(File gradleHome) {
if (isWindows()) {
return;
}
File gradleCommand = new File(gradleHome, "bin/gradle");
String errorMessage = null;
try {
ProcessBuilder pb = new ProcessBuilder("chmod", "755", gradleCommand.getCanonicalPath());
Process p = pb.start();
if (p.waitFor() != 0) {
BufferedReader is = new BufferedReader(new InputStreamReader(p.getInputStream()));
Formatter stdout = new Formatter();
String line;
while ((line = is.readLine()) != null) {
stdout.format("%s%n", line);
}
errorMessage = stdout.toString();
}
} catch (IOException e) {
errorMessage = e.getMessage();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
errorMessage = e.getMessage();
}
if (errorMessage != null) {
logger.log("Could not set executable permissions for: " + gradleCommand.getAbsolutePath());
}
}
private boolean isWindows() {
String osName = System.getProperty("os.name").toLowerCase(Locale.US);
if (osName.indexOf("windows") > -1) {
return true;
}
return false;
}
private boolean deleteDir(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
// The directory is now empty so delete it
return dir.delete();
}
private void unzip(File zip, File dest) throws IOException {
Enumeration entries;
ZipFile zipFile = new ZipFile(zip);
try {
entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
if (entry.isDirectory()) {
(new File(dest, entry.getName())).mkdirs();
continue;
}
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(new File(dest, entry.getName())));
try {
copyInputStream(zipFile.getInputStream(entry), outputStream);
} finally {
outputStream.close();
}
}
} finally {
zipFile.close();
}
}
private void copyInputStream(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) >= 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
private static class InstallCheck {
private final File gradleHome;
private final String failureMessage;
private static InstallCheck failure(String message) {
return new InstallCheck(null, message);
}
private static InstallCheck success(File gradleHome) {
return new InstallCheck(gradleHome, null);
}
private InstallCheck(File gradleHome, String failureMessage) {
this.gradleHome = gradleHome;
this.failureMessage = failureMessage;
}
private boolean isVerified() {
return gradleHome != null;
}
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright 2007-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
public class Logger implements Appendable {
private final boolean quiet;
public Logger(boolean quiet) {
this.quiet = quiet;
}
public void log(String message) {
if (!quiet) {
System.out.println(message);
}
}
public Appendable append(CharSequence csq) {
if (!quiet) {
System.out.append(csq);
}
return this;
}
public Appendable append(CharSequence csq, int start, int end) {
if (!quiet) {
System.out.append(csq, start, end);
}
return this;
}
public Appendable append(char c) {
if(!quiet) {
System.out.append(c);
}
return this;
}
}

View file

@ -0,0 +1,124 @@
/*
* Copyright 2007-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
import java.io.File;
import java.math.BigInteger;
import java.net.URI;
import java.security.MessageDigest;
public class PathAssembler {
public static final String GRADLE_USER_HOME_STRING = "GRADLE_USER_HOME";
public static final String PROJECT_STRING = "PROJECT";
private File gradleUserHome;
public PathAssembler() {
}
public PathAssembler(File gradleUserHome) {
this.gradleUserHome = gradleUserHome;
}
/**
* Determines the local locations for the distribution to use given the supplied configuration.
*/
public LocalDistribution getDistribution(WrapperConfiguration configuration) {
String baseName = getDistName(configuration.getDistribution());
String distName = removeExtension(baseName);
String rootDirName = rootDirName(distName, configuration);
File distDir = new File(getBaseDir(configuration.getDistributionBase()), configuration.getDistributionPath() + "/" + rootDirName);
File distZip = new File(getBaseDir(configuration.getZipBase()), configuration.getZipPath() + "/" + rootDirName + "/" + baseName);
return new LocalDistribution(distDir, distZip);
}
private String rootDirName(String distName, WrapperConfiguration configuration) {
String urlHash = getHash(Download.safeUri(configuration.getDistribution()).toString());
return distName + "/" + urlHash;
}
/**
* This method computes a hash of the provided {@code string}.
* <p>
* The algorithm in use by this method is as follows:
* <ol>
* <li>Compute the MD5 value of {@code string}.</li>
* <li>Truncate leading zeros (i.e., treat the MD5 value as a number).</li>
* <li>Convert to base 36 (the characters {@code 0-9a-z}).</li>
* </ol>
*/
private String getHash(String string) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] bytes = string.getBytes();
messageDigest.update(bytes);
return new BigInteger(1, messageDigest.digest()).toString(36);
} catch (Exception e) {
throw new RuntimeException("Could not hash input string.", e);
}
}
private String removeExtension(String name) {
int p = name.lastIndexOf(".");
if (p < 0) {
return name;
}
return name.substring(0, p);
}
private String getDistName(URI distUrl) {
String path = distUrl.getPath();
int p = path.lastIndexOf("/");
if (p < 0) {
return path;
}
return path.substring(p + 1);
}
private File getBaseDir(String base) {
if (base.equals(GRADLE_USER_HOME_STRING)) {
return gradleUserHome;
} else if (base.equals(PROJECT_STRING)) {
return new File(System.getProperty("user.dir"));
} else {
throw new RuntimeException("Base: " + base + " is unknown");
}
}
public static class LocalDistribution {
private final File distZip;
private final File distDir;
public LocalDistribution(File distDir, File distZip) {
this.distDir = distDir;
this.distZip = distZip;
}
/**
* Returns the location to install the distribution into.
*/
public File getDistributionDir() {
return distDir;
}
/**
* Returns the location to install the distribution ZIP file to.
*/
public File getZipFile() {
return distZip;
}
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class SystemPropertiesHandler {
static final String SYSTEM_PROP_PREFIX = "systemProp.";
public static Map<String, String> getSystemProperties(File propertiesFile) {
Map<String, String> propertyMap = new HashMap<String, String>();
if (!propertiesFile.isFile()) {
return propertyMap;
}
Properties properties = new Properties();
try {
FileInputStream inStream = new FileInputStream(propertiesFile);
try {
properties.load(inStream);
} finally {
inStream.close();
}
} catch (IOException e) {
throw new RuntimeException("Error when loading properties file=" + propertiesFile, e);
}
for (Object argument : properties.keySet()) {
if (argument.toString().startsWith(SYSTEM_PROP_PREFIX)) {
String key = argument.toString().substring(SYSTEM_PROP_PREFIX.length());
if (key.length() > 0) {
propertyMap.put(key, properties.get(argument).toString());
}
}
}
return propertyMap;
}
}

View file

@ -0,0 +1,75 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
import java.net.URI;
public class WrapperConfiguration {
private URI distribution;
private String distributionBase = PathAssembler.GRADLE_USER_HOME_STRING;
private String distributionPath = Install.DEFAULT_DISTRIBUTION_PATH;
private String distributionSha256Sum;
private String zipBase = PathAssembler.GRADLE_USER_HOME_STRING;
private String zipPath = Install.DEFAULT_DISTRIBUTION_PATH;
public URI getDistribution() {
return distribution;
}
public void setDistribution(URI distribution) {
this.distribution = distribution;
}
public String getDistributionBase() {
return distributionBase;
}
public void setDistributionBase(String distributionBase) {
this.distributionBase = distributionBase;
}
public String getDistributionPath() {
return distributionPath;
}
public void setDistributionPath(String distributionPath) {
this.distributionPath = distributionPath;
}
public String getDistributionSha256Sum() {
return distributionSha256Sum;
}
public void setDistributionSha256Sum(String distributionSha256Sum) {
this.distributionSha256Sum = distributionSha256Sum;
}
public String getZipBase() {
return zipBase;
}
public void setZipBase(String zipBase) {
this.zipBase = zipBase;
}
public String getZipPath() {
return zipPath;
}
public void setZipPath(String zipPath) {
this.zipPath = zipPath;
}
}

View file

@ -0,0 +1,138 @@
/*
* Copyright 2007-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.wrapper;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Properties;
public class WrapperExecutor {
public static final String DISTRIBUTION_URL_PROPERTY = "distributionUrl";
public static final String DISTRIBUTION_BASE_PROPERTY = "distributionBase";
public static final String DISTRIBUTION_PATH_PROPERTY = "distributionPath";
public static final String DISTRIBUTION_SHA_256_SUM = "distributionSha256Sum";
public static final String ZIP_STORE_BASE_PROPERTY = "zipStoreBase";
public static final String ZIP_STORE_PATH_PROPERTY = "zipStorePath";
private final Properties properties;
private final File propertiesFile;
private final WrapperConfiguration config = new WrapperConfiguration();
public static WrapperExecutor forProjectDirectory(File projectDir) {
return new WrapperExecutor(new File(projectDir, "gradle/wrapper/gradle-wrapper.properties"), new Properties());
}
public static WrapperExecutor forWrapperPropertiesFile(File propertiesFile) {
if (!propertiesFile.exists()) {
throw new RuntimeException(String.format("Wrapper properties file '%s' does not exist.", propertiesFile));
}
return new WrapperExecutor(propertiesFile, new Properties());
}
WrapperExecutor(File propertiesFile, Properties properties) {
this.properties = properties;
this.propertiesFile = propertiesFile;
if (propertiesFile.exists()) {
try {
loadProperties(propertiesFile, properties);
config.setDistribution(prepareDistributionUri());
config.setDistributionBase(getProperty(DISTRIBUTION_BASE_PROPERTY, config.getDistributionBase()));
config.setDistributionPath(getProperty(DISTRIBUTION_PATH_PROPERTY, config.getDistributionPath()));
config.setDistributionSha256Sum(getProperty(DISTRIBUTION_SHA_256_SUM, config.getDistributionSha256Sum(), false));
config.setZipBase(getProperty(ZIP_STORE_BASE_PROPERTY, config.getZipBase()));
config.setZipPath(getProperty(ZIP_STORE_PATH_PROPERTY, config.getZipPath()));
} catch (Exception e) {
throw new RuntimeException(String.format("Could not load wrapper properties from '%s'.", propertiesFile), e);
}
}
}
private URI prepareDistributionUri() throws URISyntaxException {
URI source = readDistroUrl();
if (source.getScheme() == null) {
//no scheme means someone passed a relative url. In our context only file relative urls make sense.
return new File(propertiesFile.getParentFile(), source.getSchemeSpecificPart()).toURI();
} else {
return source;
}
}
private URI readDistroUrl() throws URISyntaxException {
if (properties.getProperty(DISTRIBUTION_URL_PROPERTY) == null) {
reportMissingProperty(DISTRIBUTION_URL_PROPERTY);
}
return new URI(getProperty(DISTRIBUTION_URL_PROPERTY));
}
private static void loadProperties(File propertiesFile, Properties properties) throws IOException {
InputStream inStream = new FileInputStream(propertiesFile);
try {
properties.load(inStream);
} finally {
inStream.close();
}
}
/**
* Returns the distribution which this wrapper will use. Returns null if no wrapper meta-data was found in the specified project directory.
*/
public URI getDistribution() {
return config.getDistribution();
}
/**
* Returns the configuration for this wrapper.
*/
public WrapperConfiguration getConfiguration() {
return config;
}
public void execute(String[] args, Install install, BootstrapMainStarter bootstrapMainStarter) throws Exception {
File gradleHome = install.createDist(config);
bootstrapMainStarter.start(args, gradleHome);
}
private String getProperty(String propertyName) {
return getProperty(propertyName, null, true);
}
private String getProperty(String propertyName, String defaultValue) {
return getProperty(propertyName, defaultValue, true);
}
private String getProperty(String propertyName, String defaultValue, boolean required) {
String value = properties.getProperty(propertyName);
if (value != null) {
return value;
}
if (defaultValue != null) {
return defaultValue;
}
if (required) {
return reportMissingProperty(propertyName);
} else {
return null;
}
}
private String reportMissingProperty(String propertyName) {
throw new RuntimeException(String.format(
"No value with key '%s' specified in wrapper properties file '%s'.", propertyName, propertiesFile));
}
}

108
debian/patches/0001-No-Android-Q.patch vendored Normal file
View file

@ -0,0 +1,108 @@
diff --git a/server/src/main/java/com/genymobile/scrcpy/Controller.java b/server/src/main/java/com/genymobile/scrcpy/Controller.java
index 9100a9d..572a1b2 100644
--- a/server/src/main/java/com/genymobile/scrcpy/Controller.java
+++ b/server/src/main/java/com/genymobile/scrcpy/Controller.java
@@ -262,8 +262,8 @@ public class Controller {
}
// On Android >= 7, also press the PASTE key if requested
- if (paste && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && device.supportsInputEvents()) {
- device.injectKeycode(KeyEvent.KEYCODE_PASTE);
+ if (paste && Build.VERSION.SDK_INT >= 24 && device.supportsInputEvents()) {
+ device.injectKeycode(279);
}
return ok;
diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java
index f23dd05..6be0652 100644
--- a/server/src/main/java/com/genymobile/scrcpy/Device.java
+++ b/server/src/main/java/com/genymobile/scrcpy/Device.java
@@ -110,7 +110,7 @@ public final class Device {
}
// main display or any display on Android >= Q
- supportsInputEvents = displayId == 0 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
+ supportsInputEvents = displayId == 0 || Build.VERSION.SDK_INT >= 29;
if (!supportsInputEvents) {
Ln.w("Input events are not supported for secondary displays before Android 10");
}
diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java
index e25b6e9..9566de6 100644
--- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java
+++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java
@@ -22,7 +22,7 @@ public class ClipboardManager {
private Method getGetPrimaryClipMethod() throws NoSuchMethodException {
if (getPrimaryClipMethod == null) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < 29) {
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class);
} else {
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class);
@@ -33,7 +33,7 @@ public class ClipboardManager {
private Method getSetPrimaryClipMethod() throws NoSuchMethodException {
if (setPrimaryClipMethod == null) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < 29) {
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class);
} else {
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, int.class);
@@ -43,7 +43,7 @@ public class ClipboardManager {
}
private static ClipData getPrimaryClip(Method method, IInterface manager) throws InvocationTargetException, IllegalAccessException {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < 29) {
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME);
}
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
@@ -51,7 +51,7 @@ public class ClipboardManager {
private static void setPrimaryClip(Method method, IInterface manager, ClipData clipData)
throws InvocationTargetException, IllegalAccessException {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < 29) {
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME);
} else {
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
@@ -86,7 +86,7 @@ public class ClipboardManager {
private static void addPrimaryClipChangedListener(Method method, IInterface manager, IOnPrimaryClipChangedListener listener)
throws InvocationTargetException, IllegalAccessException {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < 29) {
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME);
} else {
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
@@ -95,7 +95,7 @@ public class ClipboardManager {
private Method getAddPrimaryClipChangedListener() throws NoSuchMethodException {
if (addPrimaryClipChangedListener == null) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < 29) {
addPrimaryClipChangedListener = manager.getClass()
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class);
} else {
diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java
index 8fbb860..de09834 100644
--- a/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java
+++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java
@@ -88,7 +88,7 @@ public final class SurfaceControl {
if (getBuiltInDisplayMethod == null) {
// the method signature has changed in Android Q
// <https://github.com/Genymobile/scrcpy/issues/586>
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < 29) {
getBuiltInDisplayMethod = CLASS.getMethod("getBuiltInDisplay", int.class);
} else {
getBuiltInDisplayMethod = CLASS.getMethod("getInternalDisplayToken");
@@ -101,7 +101,7 @@ public final class SurfaceControl {
try {
Method method = getGetBuiltInDisplayMethod();
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < 29) {
// call getBuiltInDisplay(0)
return (IBinder) method.invoke(null, 0);
}

1
debian/patches/series vendored Normal file
View file

@ -0,0 +1 @@
0001-No-Android-Q.patch

49
debian/rules vendored Executable file
View file

@ -0,0 +1,49 @@
#!/usr/bin/make -f
#export DH_VERBOSE = 1
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
export ANDROID_HOME=/usr/lib/android-sdk
export JAVA_HOME=/usr/lib/jvm/default-java
export JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF8
include /usr/share/dpkg/pkg-info.mk
%:
dh $@
build:
dh build-arch
dh build-indep --buildsystem=makefile
build-arch:
dh build-arch
build-indep:
dh build-indep --buildsystem=makefile
clean:
dh clean
override_dh_auto_clean:
dh_auto_clean
rm -rf build_manual
override_dh_auto_configure-arch:
dh_auto_configure -a -- -Dcompile_server=false
override_dh_auto_build-indep:
ANDROID_PLATFORM=23 ANDROID_BUILD_TOOLS=debian server/build_without_gradle.sh
# dirty hack for reproducible jar build
cd build_manual; \
unzip scrcpy-server; \
touch -t $$(date --date="@${SOURCE_DATE_EPOCH}" +%Y%m%d%H%M.%S) META-INF META-INF/MANIFEST.MF classes.dex; \
chmod 755 META-INF; \
chmod 644 META-INF/MANIFEST.MF classes.dex; \
TZ=UTC zip -X scrcpy-server.zip META-INF/MANIFEST.MF classes.dex; \
mv scrcpy-server.zip scrcpy-server

1
debian/scrcpy-server.install vendored Normal file
View file

@ -0,0 +1 @@
build_manual/scrcpy-server usr/share/scrcpy

2
debian/scrcpy.install vendored Normal file
View file

@ -0,0 +1,2 @@
usr/bin/scrcpy
usr/share/man/

1
debian/source/format vendored Normal file
View file

@ -0,0 +1 @@
3.0 (quilt)

2
debian/tests/control vendored Normal file
View file

@ -0,0 +1,2 @@
Tests: unittest.sh
Depends: @builddeps@

8
debian/tests/unittest.sh vendored Executable file
View file

@ -0,0 +1,8 @@
#!/bin/sh
set -e
mkdir build
cd build
meson .. --wrap-mode=nodownload -Dcompile_server=false
ninja test
rm -rf build

4
debian/upstream/metadata vendored Normal file
View file

@ -0,0 +1,4 @@
Bug-Submit: https://github.com/Genymobile/scrcpy/issues
Repository: https://github.com/Genymobile/scrcpy.git
Repository-Browse: https://github.com/Genymobile/scrcpy
Screenshots: https://raw.githubusercontent.com/Genymobile/scrcpy/master/assets/screenshot-debian-600.jpg

4
debian/watch vendored Normal file
View file

@ -0,0 +1,4 @@
version=4
opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%scrcpy-$1.tar.gz%" \
https://github.com/Genymobile/scrcpy/tags \
(?:.*?/)?v?(\d[\d.]*)\.tar\.gz debian uupdate