Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4b82a2ef3f |
9
.gitignore
vendored
@@ -1,7 +1,4 @@
|
||||
local.properties
|
||||
.gradle/
|
||||
.idea/
|
||||
out/
|
||||
build/
|
||||
target/
|
||||
classes/
|
||||
.gradle
|
||||
.idea
|
||||
out
|
||||
|
@@ -1,3 +0,0 @@
|
||||
REVIEWBOARD_URL = "https://git.reviewboard.kde.org"
|
||||
REPOSITORY = 'git://anongit.kde.org/kdeconnect-android'
|
||||
TARGET_GROUPS = 'kdeconnect'
|
339
COPYING
@@ -1,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
1
KdeConnect/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
build/
|
79
KdeConnect/KdeConnect.iml
Normal file
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="Debug" />
|
||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleTest" />
|
||||
<option name="SOURCE_GEN_TASK_NAME" value="TODO" />
|
||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
||||
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
|
||||
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
<option name="GRADLE_PROJECT_PATH" value=":KdeConnect" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/build/classes/debug" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/source/r/debug" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/source/aidl/debug" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/source/rs/debug" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/source/buildConfig/debug" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/res/rs/debug" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/source/r/test" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/source/aidl/test" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/source/rs/test" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/source/buildConfig/test" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/res/rs/test" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/res" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/assets" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/res" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/resources" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/apk" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/bundles" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/dependency-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/exploded-bundles" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/libs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android 4.3 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="android-support-v4" level="project" />
|
||||
<orderEntry type="library" exported="" name="mina-core-2.0.7" level="project" />
|
||||
<orderEntry type="library" exported="" name="slf4j-api-1.6.6" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-18.0.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="ComAndroidSupportAppcompatV71800.aar" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
|
26
KdeConnect/build.gradle
Normal file
@@ -0,0 +1,26 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.5.+'
|
||||
}
|
||||
}
|
||||
apply plugin: 'android'
|
||||
|
||||
dependencies {
|
||||
compile "com.android.support:appcompat-v7:18.0.+"
|
||||
compile files('libs/android-support-v4.jar')
|
||||
compile files('libs/mina-core-2.0.7.jar')
|
||||
compile files('libs/slf4j-api-1.6.6.jar')
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 18
|
||||
buildToolsVersion "18.0.1"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 18
|
||||
}
|
||||
}
|
BIN
KdeConnect/libs/android-support-v7-appcompat.jar
Normal file
BIN
KdeConnect/libs/mina-core-2.0.7.jar
Normal file
BIN
KdeConnect/libs/slf4j-api-1.6.6.jar
Normal file
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.kde.kdeconnect_tp"
|
||||
android:versionCode="720"
|
||||
android:versionName="0.7.2">
|
||||
android:versionCode="4"
|
||||
android:versionName="0.2.1">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9"
|
||||
android:targetSdkVersion="19" />
|
||||
android:targetSdkVersion="18" />
|
||||
|
||||
<supports-screens
|
||||
android:smallScreens="true"
|
||||
@@ -18,15 +18,13 @@
|
||||
<uses-feature android:name="android.hardware.telephony" android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" android:required="false" />
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" android:required="false"/>
|
||||
<uses-permission android:name="android.permission.BATTERY_STATS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_SMS" android:required="false" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_SMS" android:required="false"/>
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -34,11 +32,6 @@
|
||||
android:label="KDE Connect"
|
||||
>
|
||||
|
||||
<service
|
||||
android:enabled="true"
|
||||
android:name="org.kde.kdeconnect.BackgroundService">
|
||||
</service>
|
||||
|
||||
<activity
|
||||
android:theme="@style/Theme.AppCompat"
|
||||
android:name="org.kde.kdeconnect.UserInterface.MainActivity"
|
||||
@@ -46,48 +39,66 @@
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="org.kde.kdeconnect.UserInterface.MainSettingsActivity"
|
||||
android:label="@string/settings"
|
||||
android:parentActivityName="org.kde.kdeconnect.UserInterface.MainActivity"
|
||||
>
|
||||
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.kde.kdeconnect.UserInterface.MainActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:theme="@style/Theme.AppCompat"
|
||||
android:name="org.kde.kdeconnect.UserInterface.DeviceActivity"
|
||||
android:label="@string/device"
|
||||
android:parentActivityName="org.kde.kdeconnect.UserInterface.MainActivity"
|
||||
android:parentActivityName="org.kde.connect.UserInterface.MainActivity"
|
||||
>
|
||||
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.kde.kdeconnect.UserInterface.MainActivity" />
|
||||
</activity>
|
||||
android:value="org.kde.connect.UserInterface.MainActivity" />
|
||||
|
||||
</activity>
|
||||
|
||||
|
||||
|
||||
<activity
|
||||
android:theme="@style/Theme.AppCompat"
|
||||
android:name="org.kde.kdeconnect.UserInterface.PairActivity"
|
||||
android:label="@string/pair_device"
|
||||
android:parentActivityName="org.kde.kdeconnect.UserInterface.MainActivity"
|
||||
android:parentActivityName="org.kde.connect.UserInterface.MainActivity"
|
||||
>
|
||||
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.kde.kdeconnect.UserInterface.MainActivity" />
|
||||
android:value="org.kde.connect.UserInterface.MainActivity" />
|
||||
|
||||
</activity>
|
||||
|
||||
|
||||
|
||||
<activity
|
||||
android:theme="@style/Theme.AppCompat"
|
||||
android:name="org.kde.kdeconnect.Plugins.MprisPlugin.MprisActivity"
|
||||
android:label="@string/remote_control"
|
||||
>
|
||||
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".DeviceActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="org.kde.kdeconnect.UserInterface.SettingsActivity"
|
||||
android:label="@string/settings"
|
||||
android:parentActivityName="org.kde.kdeconnect.UserInterface.DeviceActivity"
|
||||
>
|
||||
android:name="org.kde.kdeconnect.UserInterface.SettingsActivity"
|
||||
android:label="@string/settings"
|
||||
>
|
||||
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" />
|
||||
android:value=".DeviceActivity" />
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:enabled="true"
|
||||
android:name="org.kde.kdeconnect.BackgroundService">
|
||||
</service>
|
||||
|
||||
<service android:name="org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationReceiver"
|
||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.notification.NotificationListenerService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver android:name="org.kde.kdeconnect.KdeConnectBroadcastReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||
@@ -113,54 +124,6 @@
|
||||
</intent-filter>-->
|
||||
</receiver>
|
||||
|
||||
<!-- Plugin-related activities and services -->
|
||||
|
||||
<activity
|
||||
android:theme="@style/Theme.AppCompat"
|
||||
android:name="org.kde.kdeconnect.Plugins.MprisPlugin.MprisActivity"
|
||||
android:label="@string/remote_control"
|
||||
android:parentActivityName="org.kde.kdeconnect.UserInterface.DeviceActivity"
|
||||
>
|
||||
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:theme="@style/Theme.AppCompat"
|
||||
android:name="org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadActivity"
|
||||
android:label="@string/remote_control"
|
||||
android:screenOrientation="fullSensor"
|
||||
android:parentActivityName="org.kde.kdeconnect.UserInterface.DeviceActivity"
|
||||
>
|
||||
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:theme="@style/Theme.AppCompat"
|
||||
android:name="org.kde.kdeconnect.UserInterface.ShareToReceiver"
|
||||
android:label="KDE Connect"
|
||||
>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service android:name="org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationReceiver"
|
||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.notification.NotificationListenerService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
@@ -11,10 +11,10 @@ import android.preference.PreferenceManager;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
|
||||
import org.kde.kdeconnect.UserInterface.MainSettingsActivity;
|
||||
import org.kde.kdeconnect.ComputerLinks.BaseComputerLink;
|
||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.LinkProviders.LanLinkProvider;
|
||||
import org.kde.kdeconnect.LinkProviders.LoopbackLinkProvider;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
@@ -26,11 +26,11 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class BackgroundService extends Service {
|
||||
|
||||
private final ArrayList<BaseLinkProvider> linkProviders = new ArrayList<BaseLinkProvider>();
|
||||
private ArrayList<BaseLinkProvider> linkProviders = new ArrayList<BaseLinkProvider>();
|
||||
|
||||
private final HashMap<String, Device> devices = new HashMap<String, Device>();
|
||||
private HashMap<String, Device> devices = new HashMap<String, Device>();
|
||||
|
||||
private final Device.PairingCallback devicePairingCallback = new Device.PairingCallback() {
|
||||
private Device.PairingCallback devicePairingCallback = new Device.PairingCallback() {
|
||||
@Override
|
||||
public void incomingRequest() {
|
||||
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged();
|
||||
@@ -50,20 +50,18 @@ public class BackgroundService extends Service {
|
||||
};
|
||||
|
||||
private void loadRememberedDevicesFromSettings() {
|
||||
//Log.e("BackgroundService", "Loading remembered trusted devices");
|
||||
SharedPreferences preferences = getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
Set<String> trustedDevices = preferences.getAll().keySet();
|
||||
for(String deviceId : trustedDevices) {
|
||||
//Log.e("BackgroundService", "Loading device "+deviceId);
|
||||
if (preferences.getBoolean(deviceId, false)) {
|
||||
Device device = new Device(this, deviceId);
|
||||
Device device = new Device(getBaseContext(), deviceId);
|
||||
devices.put(deviceId,device);
|
||||
device.addPairingCallback(devicePairingCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerLinkProviders() {
|
||||
public void registerLinkProviders() {
|
||||
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
@@ -81,9 +79,9 @@ public class BackgroundService extends Service {
|
||||
return devices.get(id);
|
||||
}
|
||||
|
||||
private final BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
|
||||
private BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
|
||||
@Override
|
||||
public void onConnectionReceived(final NetworkPackage identityPackage, final BaseLink link) {
|
||||
public void onConnectionReceived(final NetworkPackage identityPackage, final BaseComputerLink link) {
|
||||
|
||||
Log.i("BackgroundService", "Connection accepted!");
|
||||
|
||||
@@ -93,10 +91,11 @@ public class BackgroundService extends Service {
|
||||
|
||||
if (device != null) {
|
||||
Log.i("BackgroundService", "addLink, known device: " + deviceId);
|
||||
device.addLink(identityPackage, link);
|
||||
device.addLink(link);
|
||||
} else {
|
||||
Log.i("BackgroundService", "addLink,unknown device: " + deviceId);
|
||||
device = new Device(BackgroundService.this, identityPackage, link);
|
||||
String name = identityPackage.getString("deviceName");
|
||||
device = new Device(getBaseContext(), deviceId, name, link);
|
||||
devices.put(deviceId, device);
|
||||
device.addPairingCallback(devicePairingCallback);
|
||||
}
|
||||
@@ -105,13 +104,12 @@ public class BackgroundService extends Service {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost(BaseLink link) {
|
||||
public void onConnectionLost(BaseComputerLink link) {
|
||||
Device d = devices.get(link.getDeviceId());
|
||||
Log.i("onConnectionLost", "removeLink, deviceId: " + link.getDeviceId());
|
||||
if (d != null) {
|
||||
d.removeLink(link);
|
||||
if (!d.isReachable() && !d.isPaired()) {
|
||||
//Log.e("onConnectionLost","Removing connection device because it was not paired");
|
||||
devices.remove(link.getDeviceId());
|
||||
d.removePairingCallback(devicePairingCallback);
|
||||
}
|
||||
@@ -181,7 +179,6 @@ public class BackgroundService extends Service {
|
||||
Log.i("BackgroundService","Service not started yet, initializing...");
|
||||
|
||||
initializeRsaKeys();
|
||||
MainSettingsActivity.initializeDeviceName(this);
|
||||
loadRememberedDevicesFromSettings();
|
||||
registerLinkProviders();
|
||||
|
||||
@@ -265,9 +262,9 @@ public class BackgroundService extends Service {
|
||||
void onServiceStart(BackgroundService service);
|
||||
}
|
||||
|
||||
private final static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
|
||||
private static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
|
||||
|
||||
private final static Lock mutex = new ReentrantLock(true);
|
||||
private static final Lock mutex = new ReentrantLock(true);
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
@@ -1,20 +1,18 @@
|
||||
package org.kde.kdeconnect.Backends;
|
||||
package org.kde.kdeconnect.ComputerLinks;
|
||||
|
||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
public abstract class BaseLink {
|
||||
public abstract class BaseComputerLink {
|
||||
|
||||
private final BaseLinkProvider linkProvider;
|
||||
private final String deviceId;
|
||||
private final ArrayList<PackageReceiver> receivers = new ArrayList<PackageReceiver>();
|
||||
protected PrivateKey privateKey;
|
||||
private BaseLinkProvider linkProvider;
|
||||
private String deviceId;
|
||||
private ArrayList<PackageReceiver> receivers = new ArrayList<PackageReceiver>();
|
||||
|
||||
protected BaseLink(String deviceId, BaseLinkProvider linkProvider) {
|
||||
protected BaseComputerLink(String deviceId, BaseLinkProvider linkProvider) {
|
||||
this.linkProvider = linkProvider;
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
@@ -23,10 +21,6 @@ public abstract class BaseLink {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setPrivateKey(PrivateKey key) {
|
||||
privateKey = key;
|
||||
}
|
||||
|
||||
public BaseLinkProvider getLinkProvider() {
|
||||
return linkProvider;
|
||||
}
|
||||
@@ -50,8 +44,7 @@ public abstract class BaseLink {
|
||||
}
|
||||
}
|
||||
|
||||
//TO OVERRIDE, should be sync
|
||||
//TO OVERRIDE
|
||||
public abstract boolean sendPackage(NetworkPackage np);
|
||||
public abstract boolean sendPackageEncrypted(NetworkPackage np, PublicKey key);
|
||||
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package org.kde.kdeconnect.ComputerLinks;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
|
||||
public class LanComputerLink extends BaseComputerLink {
|
||||
|
||||
private IoSession session = null;
|
||||
|
||||
public void disconnect() {
|
||||
Log.i("LanComputerLink","Disconnect: "+session.getRemoteAddress().toString());
|
||||
session.close(true);
|
||||
}
|
||||
|
||||
public LanComputerLink(IoSession session, String deviceId, BaseLinkProvider linkProvider) {
|
||||
super(deviceId, linkProvider);
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendPackage(NetworkPackage np) {
|
||||
if (session == null) {
|
||||
Log.e("LanComputerLink","sendPackage failed: not yet connected");
|
||||
return false;
|
||||
} else {
|
||||
session.write(np.serialize());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void injectNetworkPackage(NetworkPackage np) {
|
||||
packageReceived(np);
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package org.kde.kdeconnect.ComputerLinks;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
|
||||
public class LoopbackComputerLink extends BaseComputerLink {
|
||||
|
||||
public LoopbackComputerLink(BaseLinkProvider linkProvider) {
|
||||
super("loopback", linkProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendPackage(NetworkPackage in) {
|
||||
String s = in.serialize();
|
||||
NetworkPackage out= NetworkPackage.unserialize(s);
|
||||
packageReceived(out);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@@ -7,6 +7,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.preference.PreferenceManager;
|
||||
@@ -14,7 +15,7 @@ import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.ComputerLinks.BaseComputerLink;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||
import org.kde.kdeconnect.UserInterface.PairActivity;
|
||||
@@ -33,15 +34,14 @@ import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class Device implements BaseLink.PackageReceiver {
|
||||
public class Device implements BaseComputerLink.PackageReceiver {
|
||||
|
||||
private final Context context;
|
||||
private Context context;
|
||||
|
||||
private final String deviceId;
|
||||
private final String name;
|
||||
public PublicKey publicKey;
|
||||
private String deviceId;
|
||||
private String name;
|
||||
private PublicKey publicKey;
|
||||
private int notificationId;
|
||||
private int protocolVersion;
|
||||
|
||||
private enum PairStatus {
|
||||
NotPaired,
|
||||
@@ -61,11 +61,11 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
private ArrayList<PairingCallback> pairingCallback = new ArrayList<PairingCallback>();
|
||||
private Timer pairingTimer;
|
||||
|
||||
private final ArrayList<BaseLink> links = new ArrayList<BaseLink>();
|
||||
private final HashMap<String, Plugin> plugins = new HashMap<String, Plugin>();
|
||||
private final HashMap<String, Plugin> failedPlugins = new HashMap<String, Plugin>();
|
||||
private ArrayList<BaseComputerLink> links = new ArrayList<BaseComputerLink>();
|
||||
private HashMap<String, Plugin> plugins = new HashMap<String, Plugin>();
|
||||
private HashMap<String, Plugin> failedPlugins = new HashMap<String, Plugin>();
|
||||
|
||||
private final SharedPreferences settings;
|
||||
SharedPreferences settings;
|
||||
|
||||
//Remembered trusted device, we need to wait for a incoming devicelink to communicate
|
||||
Device(Context context, String deviceId) {
|
||||
@@ -77,7 +77,6 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
this.deviceId = deviceId;
|
||||
this.name = settings.getString("deviceName", "unknown device");
|
||||
this.pairStatus = PairStatus.Paired;
|
||||
this.protocolVersion = NetworkPackage.ProtocolVersion; //We don't know it yet
|
||||
|
||||
try {
|
||||
byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0);
|
||||
@@ -91,20 +90,18 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
}
|
||||
|
||||
//Device known via an incoming connection sent to us via a devicelink, we know everything but we don't trust it yet
|
||||
Device(Context context, NetworkPackage np, BaseLink dl) {
|
||||
Device(Context context, String deviceId, String name, BaseComputerLink dl) {
|
||||
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||
|
||||
//Log.e("Device","Constructor B");
|
||||
|
||||
this.context = context;
|
||||
this.deviceId = np.getString("deviceId");
|
||||
this.name = np.getString("deviceName", "unidentified device");
|
||||
this.protocolVersion = np.getInt("protocolVersion");
|
||||
this.deviceId = deviceId;
|
||||
this.name = name;
|
||||
this.pairStatus = PairStatus.NotPaired;
|
||||
this.publicKey = null;
|
||||
|
||||
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||
|
||||
addLink(np, dl);
|
||||
addLink(dl);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -115,10 +112,6 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
//Returns 0 if the version matches, < 0 if it is older or > 0 if it is newer
|
||||
public int compareProtocolVersion() {
|
||||
return protocolVersion - NetworkPackage.ProtocolVersion;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -151,55 +144,37 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
Resources res = context.getResources();
|
||||
|
||||
if (pairStatus == PairStatus.Paired) {
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(res.getString(R.string.error_already_paired));
|
||||
}
|
||||
for (PairingCallback cb : pairingCallback) cb.pairingFailed(res.getString(R.string.error_already_paired));
|
||||
return;
|
||||
}
|
||||
if (pairStatus == PairStatus.Requested) {
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(res.getString(R.string.error_already_requested));
|
||||
}
|
||||
for (PairingCallback cb : pairingCallback) cb.pairingFailed(res.getString(R.string.error_already_requested));
|
||||
return;
|
||||
}
|
||||
if (!isReachable()) {
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(res.getString(R.string.error_not_reachable));
|
||||
}
|
||||
for (PairingCallback cb : pairingCallback) cb.pairingFailed(res.getString(R.string.error_not_reachable));
|
||||
return;
|
||||
}
|
||||
|
||||
//Send our own public key
|
||||
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
|
||||
sendPackage(np, new SendPackageFinishedCallback(){
|
||||
boolean success = sendPackage(np);
|
||||
|
||||
@Override
|
||||
public void sendSuccessful() {
|
||||
if (pairingTimer != null) pairingTimer.cancel();
|
||||
pairingTimer = new Timer();
|
||||
pairingTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(context.getString(R.string.error_timed_out));
|
||||
}
|
||||
Log.e("Device","Unpairing (timeout A)");
|
||||
pairStatus = PairStatus.NotPaired;
|
||||
}
|
||||
}, 30*1000); //Time to wait for the other to accept
|
||||
pairStatus = PairStatus.Requested;
|
||||
}
|
||||
if (!success) {
|
||||
for (PairingCallback cb : pairingCallback) cb.pairingFailed(res.getString(R.string.error_could_not_send_package));
|
||||
return;
|
||||
}
|
||||
|
||||
pairingTimer = new Timer();
|
||||
pairingTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void sendFailed() {
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(context.getString(R.string.error_could_not_send_package));
|
||||
}
|
||||
Log.e("Device","Unpairing (sendFailed A)");
|
||||
public void run() {
|
||||
for (PairingCallback cb : pairingCallback) cb.pairingFailed(context.getString(R.string.error_timed_out));
|
||||
pairStatus = PairStatus.NotPaired;
|
||||
}
|
||||
}, 20*1000);
|
||||
|
||||
});
|
||||
pairStatus = PairStatus.Requested;
|
||||
|
||||
}
|
||||
|
||||
@@ -209,7 +184,8 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
|
||||
public void unpair() {
|
||||
|
||||
//Log.e("Device","Unpairing (unpair)");
|
||||
if (!isPaired()) return;
|
||||
|
||||
pairStatus = PairStatus.NotPaired;
|
||||
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
@@ -225,70 +201,46 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
|
||||
}
|
||||
|
||||
private void pairingDone() {
|
||||
public void acceptPairing() {
|
||||
|
||||
//Log.e("Device", "Storing as trusted, deviceId: "+deviceId);
|
||||
Log.i("Device","Accepted pairing");
|
||||
|
||||
if (pairingTimer != null) pairingTimer.cancel();
|
||||
//Send our own public key
|
||||
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
|
||||
boolean success = sendPackage(np);
|
||||
|
||||
if (!success) return;
|
||||
|
||||
pairStatus = PairStatus.Paired;
|
||||
|
||||
//Store as trusted device
|
||||
String encodedPublicKey = Base64.encodeToString(publicKey.getEncoded(), 0);
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
preferences.edit().putBoolean(deviceId,true).commit();
|
||||
|
||||
//Store device information needed to create a Device object in a future
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putString("deviceName", getName());
|
||||
String encodedPublicKey = Base64.encodeToString(publicKey.getEncoded(), 0);
|
||||
editor.putString("publicKey", encodedPublicKey);
|
||||
editor.commit();
|
||||
|
||||
reloadPluginsFromSettings();
|
||||
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingSuccessful();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void acceptPairing() {
|
||||
|
||||
Log.i("Device","Accepted pair request started by the other device");
|
||||
|
||||
//Send our own public key
|
||||
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
|
||||
sendPackage(np, new SendPackageFinishedCallback() {
|
||||
@Override
|
||||
public void sendSuccessful() {
|
||||
pairingDone();
|
||||
}
|
||||
@Override
|
||||
public void sendFailed() {
|
||||
Log.e("Device","Unpairing (sendFailed B)");
|
||||
pairStatus = PairStatus.NotPaired;
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(context.getString(R.string.error_not_reachable));
|
||||
}
|
||||
}
|
||||
});
|
||||
for (PairingCallback cb : pairingCallback) cb.pairingSuccessful();
|
||||
|
||||
}
|
||||
|
||||
public void rejectPairing() {
|
||||
|
||||
Log.i("Device","Rejected pair request started by the other device");
|
||||
Log.i("Device","Rejected pairing");
|
||||
|
||||
//Log.e("Device","Unpairing (rejectPairing)");
|
||||
pairStatus = PairStatus.NotPaired;
|
||||
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
|
||||
np.set("pair", false);
|
||||
sendPackage(np);
|
||||
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(context.getString(R.string.error_canceled_by_user));
|
||||
}
|
||||
for (PairingCallback cb : pairingCallback) cb.pairingFailed(context.getString(R.string.error_canceled_by_user));
|
||||
|
||||
}
|
||||
|
||||
@@ -303,27 +255,15 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
return !links.isEmpty();
|
||||
}
|
||||
|
||||
public void addLink(NetworkPackage identityPackage, BaseLink link) {
|
||||
|
||||
this.protocolVersion = identityPackage.getInt("protocolVersion");
|
||||
public void addLink(BaseComputerLink link) {
|
||||
|
||||
links.add(link);
|
||||
|
||||
try {
|
||||
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey", ""), 0);
|
||||
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
|
||||
link.setPrivateKey(privateKey);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("Device", "Exception reading our own private key"); //Should not happen
|
||||
}
|
||||
|
||||
Log.i("Device","addLink "+link.getLinkProvider().getName()+" -> "+getName() + " active links: "+ links.size());
|
||||
|
||||
Collections.sort(links, new Comparator<BaseLink>() {
|
||||
Collections.sort(links, new Comparator<BaseComputerLink>() {
|
||||
@Override
|
||||
public int compare(BaseLink o, BaseLink o2) {
|
||||
public int compare(BaseComputerLink o, BaseComputerLink o2) {
|
||||
return o2.getLinkProvider().getPriority() - o.getLinkProvider().getPriority();
|
||||
}
|
||||
});
|
||||
@@ -335,7 +275,7 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
public void removeLink(BaseLink link) {
|
||||
public void removeLink(BaseComputerLink link) {
|
||||
link.removePackageReceiver(this);
|
||||
links.remove(link);
|
||||
Log.i("Device","removeLink: "+link.getLinkProvider().getName() + " -> "+getName() + " active links: "+ links.size());
|
||||
@@ -355,12 +295,9 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
|
||||
if (wantsPair == isPaired()) {
|
||||
if (pairStatus == PairStatus.Requested) {
|
||||
//Log.e("Device","Unpairing (pair rejected)");
|
||||
pairStatus = PairStatus.NotPaired;
|
||||
if (pairingTimer != null) pairingTimer.cancel();
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(context.getString(R.string.error_canceled_by_other_peer));
|
||||
}
|
||||
pairingTimer.cancel();
|
||||
for (PairingCallback cb : pairingCallback) cb.pairingFailed(context.getString(R.string.error_canceled_by_other_peer));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -375,9 +312,7 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("Device","Pairing exception: Received incorrect key");
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(context.getString(R.string.error_invalid_key));
|
||||
}
|
||||
for (PairingCallback cb : pairingCallback) cb.pairingFailed(context.getString(R.string.error_invalid_key));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -385,9 +320,23 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
|
||||
Log.i("Pairing","Pair answer");
|
||||
|
||||
if (pairingTimer != null) pairingTimer.cancel();
|
||||
pairStatus = PairStatus.Paired;
|
||||
pairingTimer.cancel();
|
||||
|
||||
pairingDone();
|
||||
//Store as trusted device
|
||||
String encodedPublicKey = Base64.encodeToString(publicKey.getEncoded(), 0);
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
preferences.edit().putBoolean(deviceId,true).commit();
|
||||
|
||||
//Store device information needed to create a Device object in a future
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putString("deviceName", getName());
|
||||
editor.putString("publicKey", encodedPublicKey);
|
||||
editor.commit();
|
||||
|
||||
reloadPluginsFromSettings();
|
||||
|
||||
for (PairingCallback cb : pairingCallback) cb.pairingSuccessful();
|
||||
|
||||
} else {
|
||||
|
||||
@@ -414,17 +363,15 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
notificationId = (int)System.currentTimeMillis();
|
||||
notificationManager.notify(notificationId, noti);
|
||||
|
||||
if (pairingTimer != null) pairingTimer.cancel();
|
||||
pairingTimer = new Timer();
|
||||
|
||||
pairingTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.e("Device","Unpairing (timeout B)");
|
||||
pairStatus = PairStatus.NotPaired;
|
||||
notificationManager.cancel(notificationId);
|
||||
}
|
||||
}, 25*1000); //Time to show notification, waiting for user to accept (peer will timeout in 30 seconds)
|
||||
}, 19*1000); //Time to show notification
|
||||
|
||||
pairStatus = PairStatus.RequestedByPeer;
|
||||
for (PairingCallback cb : pairingCallback) cb.incomingRequest();
|
||||
|
||||
@@ -434,97 +381,78 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
|
||||
if (pairStatus == PairStatus.Requested) {
|
||||
pairingTimer.cancel();
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(context.getString(R.string.error_canceled_by_other_peer));
|
||||
}
|
||||
for (PairingCallback cb : pairingCallback) cb.pairingFailed(context.getString(R.string.error_canceled_by_other_peer));
|
||||
} else if (pairStatus == PairStatus.Paired) {
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
preferences.edit().remove(deviceId).commit();
|
||||
reloadPluginsFromSettings();
|
||||
}
|
||||
|
||||
//Log.e("Device","Unpairing (unpair request)");
|
||||
pairStatus = PairStatus.NotPaired;
|
||||
for (PairingCallback cb : pairingCallback) cb.unpaired();
|
||||
|
||||
}
|
||||
} else if (!isPaired()) {
|
||||
|
||||
unpair();
|
||||
//TODO: Notify the other side that we don't trust them
|
||||
Log.e("onPackageReceived","Device not paired, ignoring package!");
|
||||
|
||||
} else {
|
||||
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_ENCRYPTED)) {
|
||||
|
||||
for (Plugin plugin : plugins.values()) {
|
||||
try {
|
||||
plugin.onPackageReceived(np);
|
||||
} catch (Exception e) {
|
||||
//TODO: Do not read the key every time
|
||||
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey",""), 0);
|
||||
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
|
||||
np = np.decrypt(privateKey);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("Device", "Exception in "+plugin.getDisplayName()+"'s onPackageReceived()");
|
||||
Log.e("onPackageReceived","Exception reading the key needed to decrypt the package");
|
||||
}
|
||||
|
||||
} else {
|
||||
//TODO: The other side doesn't know that we are already paired, do something
|
||||
Log.e("onPackageReceived","WARNING: Received unencrypted package from paired device!");
|
||||
}
|
||||
|
||||
for (Plugin plugin : plugins.values()) {
|
||||
plugin.onPackageReceived(np);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public interface SendPackageFinishedCallback {
|
||||
void sendSuccessful();
|
||||
void sendFailed();
|
||||
}
|
||||
|
||||
public void sendPackage(NetworkPackage np) {
|
||||
sendPackage(np,null);
|
||||
}
|
||||
|
||||
//Async
|
||||
public void sendPackage(final NetworkPackage np, final SendPackageFinishedCallback callback) {
|
||||
|
||||
|
||||
final Exception backtrace = new Exception();
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
//Log.e("sendPackage", "Sending package...");
|
||||
//Log.e("sendPackage", np.serialize());
|
||||
|
||||
boolean useEncryption = (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR) && isPaired());
|
||||
|
||||
//We need a copy to avoid concurrent modification exception if the original list changes
|
||||
ArrayList<BaseLink> mLinks = new ArrayList<BaseLink>(links);
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
for (BaseLink link : mLinks) {
|
||||
if (useEncryption) {
|
||||
success = link.sendPackageEncrypted(np, publicKey);
|
||||
} else {
|
||||
success = link.sendPackage(np);
|
||||
}
|
||||
if (success) break;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("sendPackage","Error while sending package");
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
// Log.e("sendPackage","Package sent");
|
||||
} else {
|
||||
backtrace.printStackTrace();
|
||||
Log.e("sendPackage","Error: Package could not be sent ("+mLinks.size()+" links available)");
|
||||
}
|
||||
|
||||
if (callback != null) {
|
||||
if (success) callback.sendSuccessful();
|
||||
else callback.sendFailed();
|
||||
}
|
||||
public boolean sendPackage(final NetworkPackage np) {
|
||||
|
||||
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR) && isPaired()) {
|
||||
try {
|
||||
np.encrypt(publicKey);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("Device","sendPackage exception - could not encrypt");
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
new AsyncTask<Void,Void,Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
for(BaseComputerLink link : links) {
|
||||
//Log.e("sendPackage","Trying "+link.getLinkProvider().getName());
|
||||
if (link.sendPackage(np)) {
|
||||
//Log.e("sent using", link.getLinkProvider().getName());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Log.e("sendPackage","Error: Package could not be sent ("+links.size()+" links available)");
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
|
||||
//TODO: Detect when unable to send a package and try again somehow
|
||||
|
||||
return !links.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -539,7 +467,7 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
return plugins.get(name);
|
||||
}
|
||||
|
||||
private synchronized void addPlugin(final String name) {
|
||||
private void addPlugin(final String name) {
|
||||
Plugin existing = plugins.get(name);
|
||||
if (existing != null) {
|
||||
Log.w("addPlugin","plugin already present:" + name);
|
||||
@@ -585,7 +513,7 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
|
||||
}
|
||||
|
||||
private synchronized boolean removePlugin(String name) {
|
||||
private boolean removePlugin(String name) {
|
||||
|
||||
Plugin plugin = plugins.remove(name);
|
||||
Plugin failedPlugin = failedPlugins.remove(name);
|
||||
@@ -617,7 +545,7 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
|
||||
public void setPluginEnabled(String pluginName, boolean value) {
|
||||
settings.edit().putBoolean(pluginName,value).commit();
|
||||
if (value && isPaired() && isReachable()) addPlugin(pluginName);
|
||||
if (value) addPlugin(pluginName);
|
||||
else removePlugin(pluginName);
|
||||
}
|
||||
|
||||
@@ -663,7 +591,7 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
void onPluginsChanged(Device device);
|
||||
}
|
||||
|
||||
private final ArrayList<PluginsChangedListener> pluginsChangedListeners = new ArrayList<PluginsChangedListener>();
|
||||
private ArrayList<PluginsChangedListener> pluginsChangedListeners = new ArrayList<PluginsChangedListener>();
|
||||
|
||||
public void addPluginsChangedListener(PluginsChangedListener listener) {
|
||||
pluginsChangedListeners.add(listener);
|
@@ -13,7 +13,7 @@ public class AppsHelper {
|
||||
try {
|
||||
|
||||
PackageManager pm = context.getPackageManager();
|
||||
ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
|
||||
ApplicationInfo ai = pm.getApplicationInfo( packageName, 0);
|
||||
|
||||
return pm.getApplicationLabel(ai).toString();
|
||||
|
@@ -3,7 +3,9 @@ package org.kde.kdeconnect.Helpers;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.PhoneLookup;
|
||||
import android.util.Log;
|
||||
|
||||
public class ContactsHelper {
|
||||
|
||||
@@ -29,17 +31,14 @@ public class ContactsHelper {
|
||||
|
||||
// Take the first match only
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int nameIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME);
|
||||
int nameIndex = cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME);
|
||||
if (nameIndex != -1) {
|
||||
String name = cursor.getString(nameIndex);
|
||||
//Log.e("PhoneNumberLookup", "success: " + name);
|
||||
cursor.close();
|
||||
return name + " (" + number + ")";
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor != null) cursor.close();
|
||||
|
||||
return number;
|
||||
|
||||
}
|
@@ -1,17 +1,15 @@
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
package org.kde.kdeconnect;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class DeviceHelper {
|
||||
public class HumanDeviceNames {
|
||||
|
||||
//from https://github.com/meetup/android-device-names
|
||||
//Converted to java using:
|
||||
//cat android_models.properties | awk -F'=' '{sub(/ *$/, "", $1)} sub(/^ */, "", $2) { if ($2 != "") print "humanReadableNames.put(\""$1"\",\"" $2 "\");"}'
|
||||
private final static HashMap<String,String> humanReadableNames = new HashMap<String,String>();
|
||||
private static HashMap<String,String> humanReadableNames = new HashMap<String,String>();
|
||||
static {
|
||||
humanReadableNames.put("5860E","Coolpad Quattro 4G");
|
||||
humanReadableNames.put("ADR6300","HTC Droid Incredible");
|
||||
@@ -24,9 +22,6 @@ public class DeviceHelper {
|
||||
humanReadableNames.put("C5155","Kyocera Rise");
|
||||
humanReadableNames.put("C5170","Kyocera Hydro");
|
||||
humanReadableNames.put("C6603","Sony Xperia Z");
|
||||
humanReadableNames.put("C6606","Sony Xperia Z");
|
||||
humanReadableNames.put("C6903","Sony Xperia Z1");
|
||||
humanReadableNames.put("D6503","Sony Xperia Z2");
|
||||
humanReadableNames.put("Desire_HD","HTC Desire HD");
|
||||
humanReadableNames.put("DROID2_GLOBAL","Motorola Droid 2 Global");
|
||||
humanReadableNames.put("DROID2","Motorola Droid 2");
|
||||
@@ -43,45 +38,34 @@ public class DeviceHelper {
|
||||
humanReadableNames.put("Galaxy_Nexus","Samsung Galaxy Nexus");
|
||||
humanReadableNames.put("google_sdk","Android Emulator");
|
||||
humanReadableNames.put("GT-I8160","Samsung Galaxy Ace 2");
|
||||
humanReadableNames.put("GT-I8190N","Samsung Galaxy S III Mini");
|
||||
humanReadableNames.put("GT-I8190","Samsung Galaxy S III Mini");
|
||||
humanReadableNames.put("GT-I9000","Samsung Galaxy S");
|
||||
humanReadableNames.put("GT-I9001","Samsung Galaxy S Plus");
|
||||
humanReadableNames.put("GT-I9070","Samsung Galaxy S Advance");
|
||||
humanReadableNames.put("GT-I9082","Samsung Galaxy Grand");
|
||||
humanReadableNames.put("GT-I9100M","Samsung Galaxy S II");
|
||||
humanReadableNames.put("GT-I9100P","Samsung Galaxy S II");
|
||||
humanReadableNames.put("GT-I9100","Samsung Galaxy S II");
|
||||
humanReadableNames.put("GT-I9100T","Samsung Galaxy S II");
|
||||
humanReadableNames.put("GT-I9195","Samsung Galaxy S4 Mini");
|
||||
humanReadableNames.put("GT-I9300","Samsung Galaxy S III");
|
||||
humanReadableNames.put("GT-I9300T","Samsung Galaxy S III");
|
||||
humanReadableNames.put("GT-I9305","Samsung Galaxy S III");
|
||||
humanReadableNames.put("GT-I9305T","Samsung Galaxy S III");
|
||||
humanReadableNames.put("GT-I9500","Samsung Galaxy S4");
|
||||
humanReadableNames.put("GT-I9505","Samsung Galaxy S4");
|
||||
humanReadableNames.put("GT-N5110","Samsung Galaxy Note 8.0");
|
||||
humanReadableNames.put("GT-I9505","Samsung Galaxy S 4");
|
||||
humanReadableNames.put("GT-N7000","Samsung Galaxy Note");
|
||||
humanReadableNames.put("GT-N7100","Samsung Galaxy Note II");
|
||||
humanReadableNames.put("GT-N7105","Samsung Galaxy Note II");
|
||||
humanReadableNames.put("GT-N8013","Samsung Galaxy Note 10.1");
|
||||
humanReadableNames.put("GT-P3113","Samsung Galaxy Tab 2 7.0");
|
||||
humanReadableNames.put("GT-P5113","Samsnung Galaxy Tab 2 10.1");
|
||||
humanReadableNames.put("GT-P5210","Samsung Galaxy Tab 3 10.1");
|
||||
humanReadableNames.put("GT-P7510","Samsung Galaxy Tab 10.1");
|
||||
humanReadableNames.put("GT-S5360","Samsung Galaxy Y");
|
||||
humanReadableNames.put("GT-S5570","Samsung Galaxy Mini");
|
||||
humanReadableNames.put("GT-S5830i","Samsung Galaxy Ace");
|
||||
humanReadableNames.put("GT-S5830","Samsung Galaxy Ace");
|
||||
humanReadableNames.put("GT-S7562","Samsung Galaxy S Duos");
|
||||
humanReadableNames.put("HTC6435LVW","HTC Droid DNA");
|
||||
humanReadableNames.put("HTC6500LVW","HTC One");
|
||||
humanReadableNames.put("HTC_Desire_HD_A9191","HTC Desire HD");
|
||||
humanReadableNames.put("HTCEVODesign4G","HTC Evo Design 4G");
|
||||
humanReadableNames.put("HTCEVOV4G","HTC Evo V 4G");
|
||||
humanReadableNames.put("HTCONE","HTC One");
|
||||
humanReadableNames.put("HTC_PH39100","HTC Vivid 4G");
|
||||
humanReadableNames.put("HTC_PN071","HTC One");
|
||||
humanReadableNames.put("HTC_Sensation_Z710e","HTC Sensation");
|
||||
humanReadableNames.put("HTC_VLE_U","HTC One S");
|
||||
humanReadableNames.put("KFJWA","Kindle Fire HD 8.9");
|
||||
@@ -89,17 +73,9 @@ public class DeviceHelper {
|
||||
humanReadableNames.put("KFOT","Kindle Fire");
|
||||
humanReadableNames.put("KFTT","Kindle Fire HD 7");
|
||||
humanReadableNames.put("LG-C800","LG myTouch Q");
|
||||
humanReadableNames.put("LG-D800","LG G2");
|
||||
humanReadableNames.put("LG-D801","LG G2");
|
||||
humanReadableNames.put("LG-D802","LG G2");
|
||||
humanReadableNames.put("LG-E739","LG MyTouch e739");
|
||||
humanReadableNames.put("LG-E970","LG Optimus G");
|
||||
humanReadableNames.put("LG-E980","LG Optimus G Pro");
|
||||
humanReadableNames.put("LGL55C","LG LGL55C");
|
||||
humanReadableNames.put("LG-LS840","LG Viper");
|
||||
humanReadableNames.put("LG-LS970","LG Optimus G");
|
||||
humanReadableNames.put("LG-LS980","LG G2");
|
||||
humanReadableNames.put("LGMS769","LG Optimus L9");
|
||||
humanReadableNames.put("LG-MS770","LG Motion 4G");
|
||||
humanReadableNames.put("LG-MS910","LG Esteem");
|
||||
humanReadableNames.put("LG-P509","LG Optimus T");
|
||||
@@ -115,20 +91,18 @@ public class DeviceHelper {
|
||||
humanReadableNames.put("MOTWX435KT","Motorola Triumph");
|
||||
humanReadableNames.put("myTouch_4G_Slide","HTC myTouch 4G Slide");
|
||||
humanReadableNames.put("N860","ZTE Warp N860");
|
||||
humanReadableNames.put("Nexus_10","Nexus 10");
|
||||
humanReadableNames.put("Nexus_4","Nexus 4");
|
||||
humanReadableNames.put("Nexus_5","Nexus 5");
|
||||
humanReadableNames.put("Nexus_7","Nexus 7");
|
||||
humanReadableNames.put("Nexus_S_4G","Nexus S 4G");
|
||||
humanReadableNames.put("Nexus_S","Nexus S");
|
||||
humanReadableNames.put("Nexus_10","Google Nexus 10");
|
||||
humanReadableNames.put("Nexus_4","Google Nexus 4");
|
||||
humanReadableNames.put("Nexus_7","Asus Nexus 7");
|
||||
humanReadableNames.put("Nexus_S_4G","Samsung Nexus S 4G");
|
||||
humanReadableNames.put("Nexus_S","Samsung Nexus S");
|
||||
humanReadableNames.put("PantechP9070","Pantech Burst");
|
||||
humanReadableNames.put("PC36100","HTC Evo 4G");
|
||||
humanReadableNames.put("PG06100","HTC EVO Shift 4G");
|
||||
humanReadableNames.put("PG86100","HTC Evo 3D");
|
||||
humanReadableNames.put("PH44100","HTC Evo Design 4G");
|
||||
humanReadableNames.put("SAMSUNG-SGH-I317","Samsung Galaxy Note II");
|
||||
humanReadableNames.put("SAMSUNG-SGH-I337","Samsung Galaxy S4");
|
||||
humanReadableNames.put("SAMSUNG-SGH-I537","Samsung Galaxy S4 Active");
|
||||
humanReadableNames.put("SAMSUNG-SGH-I337","Samsung Galaxy S 4");
|
||||
humanReadableNames.put("SAMSUNG-SGH-I717","Samsung Galaxy Note");
|
||||
humanReadableNames.put("SAMSUNG-SGH-I727","Samsung Skyrocket");
|
||||
humanReadableNames.put("SAMSUNG-SGH-I747","Samsung Galaxy S III");
|
||||
@@ -136,27 +110,22 @@ public class DeviceHelper {
|
||||
humanReadableNames.put("SAMSUNG-SGH-I897","Samsung Captivate");
|
||||
humanReadableNames.put("SAMSUNG-SGH-I927","Samsung Captivate Glide");
|
||||
humanReadableNames.put("SAMSUNG-SGH-I997","Samsung Infuse 4G");
|
||||
humanReadableNames.put("SAMSUNG-SM-N900A","Samsung Galaxy Note 3");
|
||||
humanReadableNames.put("SCH-I200","Samsung Galaxy Stellar");
|
||||
humanReadableNames.put("SCH-I405","Samsung Stratosphere");
|
||||
humanReadableNames.put("SCH-I415","Samsung Galaxy Stratosphere II");
|
||||
humanReadableNames.put("SCH-I500","Samsung Fascinate");
|
||||
humanReadableNames.put("SCH-I510","Samsung Droid Charge");
|
||||
humanReadableNames.put("SCH-I535","Samsung Galaxy S III");
|
||||
humanReadableNames.put("SCH-I545","Samsung Galaxy S4");
|
||||
humanReadableNames.put("SCH-I545","Samsung Galaxy S 4");
|
||||
humanReadableNames.put("SCH-I605","Samsung Galaxy Note II");
|
||||
humanReadableNames.put("SCH-I800","Samsung Galaxy Tab 7.0");
|
||||
humanReadableNames.put("SCH-R530M","Samsung Galaxy S III");
|
||||
humanReadableNames.put("SCH-R530U","Samsung Galaxy S III");
|
||||
humanReadableNames.put("SCH-R720","Samsung Admire");
|
||||
humanReadableNames.put("SCH-S720C","Samsung Proclaim");
|
||||
humanReadableNames.put("SCH-S738C","Samsung Galaxy Centura");
|
||||
humanReadableNames.put("SGH-I317M","Samsung Galaxy Note II");
|
||||
humanReadableNames.put("SGH-I337M","Samsung Galaxy S4");
|
||||
humanReadableNames.put("SGH-I727R","Samsung Galaxy S II");
|
||||
humanReadableNames.put("SGH-I747M","Samsung Galaxy S III");
|
||||
humanReadableNames.put("SGH-M919","Samsung Galaxy S4");
|
||||
humanReadableNames.put("SGH-T599N","Samsung Galaxy Exhibit");
|
||||
humanReadableNames.put("SGH-M919","Samsung Galaxy S 4");
|
||||
humanReadableNames.put("SGH-T679","Samsung Exhibit II");
|
||||
humanReadableNames.put("SGH-T769","Samsung Galaxy S Blaze");
|
||||
humanReadableNames.put("SGH-T889","Samsung Galaxy Note II");
|
||||
@@ -164,48 +133,28 @@ public class DeviceHelper {
|
||||
humanReadableNames.put("SGH-T959V","Samsung Galaxy S 4G");
|
||||
humanReadableNames.put("SGH-T989D","Samsung Galaxy S II");
|
||||
humanReadableNames.put("SGH-T989","Samsung Galaxy S II");
|
||||
humanReadableNames.put("SGH-T999L","Samsung Galaxy S III");
|
||||
humanReadableNames.put("SGH-T999","Samsung Galaxy S III");
|
||||
humanReadableNames.put("SGH-T999V","Samsung Galaxy S III");
|
||||
humanReadableNames.put("SHV-E210S","Samsung Galaxy S III");
|
||||
humanReadableNames.put("SM-N9005","Samsung Galaxy Note 3");
|
||||
humanReadableNames.put("SM-N900P","Samsung Galaxy Note 3");
|
||||
humanReadableNames.put("SM-N900T","Samsung Galaxy Note 3");
|
||||
humanReadableNames.put("SM-N900V","Samsung Galaxy Note 3");
|
||||
humanReadableNames.put("SM-N900W8","Samsung Galaxy Note 3");
|
||||
humanReadableNames.put("SM-T210R","Samsung Galaxy Tab 3 7.0");
|
||||
humanReadableNames.put("SM-T310","Samsung Galaxy Tab 3 8.0");
|
||||
humanReadableNames.put("SPH-D600","Samsung Conquer 4G");
|
||||
humanReadableNames.put("SPH-D700","Samsung Epic 4G");
|
||||
humanReadableNames.put("SPH-D710BST","Samsung Galaxy S II");
|
||||
humanReadableNames.put("SPH-D710","Samsung Epic");
|
||||
humanReadableNames.put("SPH-D710VMUB","Samsung Galaxy S II");
|
||||
humanReadableNames.put("SPH-L300","Samsung Galaxy Victory");
|
||||
humanReadableNames.put("SPH-L710","Samsung Galaxy S III");
|
||||
humanReadableNames.put("SPH-L720","Samsung Galaxy S4");
|
||||
humanReadableNames.put("SPH-L720","Samsung Galaxy S 4");
|
||||
humanReadableNames.put("SPH-L900","Samsung Galaxy Note II");
|
||||
humanReadableNames.put("SPH-M820-BST","Samsung Galaxy Prevail");
|
||||
humanReadableNames.put("SPH-M830","Samsung Galaxy Rush");
|
||||
humanReadableNames.put("SPH-M930BST","Samsung Transform Ultra");
|
||||
humanReadableNames.put("ST25i","Sony Xperia U");
|
||||
humanReadableNames.put("Transformer_Prime_TF201","Asus Eee Pad Transformer Prime");
|
||||
humanReadableNames.put("Transformer_TF101","Asus Eee Pad Transformer");
|
||||
humanReadableNames.put("VM670","LG Optimus V");
|
||||
humanReadableNames.put("VS840_4G","LG Lucid 4G");
|
||||
humanReadableNames.put("VS910_4G","LG Revolution 4G");
|
||||
humanReadableNames.put("VS920_4G","LG Spectrum 4G");
|
||||
humanReadableNames.put("VS980_4G","LG G2");
|
||||
humanReadableNames.put("Xoom","Motorola Xoom");
|
||||
humanReadableNames.put("XT1030","Motorola Droid Mini");
|
||||
humanReadableNames.put("XT1032","Motorola Moto G");
|
||||
humanReadableNames.put("XT1058","Motorola Moto X");
|
||||
humanReadableNames.put("XT1060","Motorola Moto X");
|
||||
humanReadableNames.put("XT1080","Motorola Droid Ultra");
|
||||
humanReadableNames.put("XT897","Motorola Photo Q");
|
||||
humanReadableNames.put("XT907","Motorola Droid Razr M");
|
||||
}
|
||||
|
||||
public static String getDeviceName() {
|
||||
static String getDeviceName() {
|
||||
|
||||
String dictName = humanReadableNames.get(Build.MODEL.replace(' ','_'));
|
||||
if (dictName != null) return dictName;
|
||||
@@ -218,11 +167,4 @@ public class DeviceHelper {
|
||||
|
||||
}
|
||||
|
||||
public static boolean isTablet() {
|
||||
Configuration config = Resources.getSystem().getConfiguration();
|
||||
//This assumes that the values for the screen sizes are consecutive, so XXLARGE > XLARGE > LARGE
|
||||
boolean isLarge = ((config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE);
|
||||
return isLarge;
|
||||
}
|
||||
|
||||
}
|
@@ -1,19 +1,19 @@
|
||||
package org.kde.kdeconnect.Backends;
|
||||
package org.kde.kdeconnect.LinkProviders;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.ComputerLinks.BaseComputerLink;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public abstract class BaseLinkProvider {
|
||||
|
||||
private final ArrayList<ConnectionReceiver> connectionReceivers = new ArrayList<ConnectionReceiver>();
|
||||
private ArrayList<ConnectionReceiver> connectionReceivers = new ArrayList<ConnectionReceiver>();
|
||||
|
||||
public interface ConnectionReceiver {
|
||||
public void onConnectionReceived(NetworkPackage identityPackage, BaseLink link);
|
||||
public void onConnectionLost(BaseLink link);
|
||||
public void onConnectionReceived(NetworkPackage identityPackage, BaseComputerLink link);
|
||||
public void onConnectionLost(BaseComputerLink link);
|
||||
}
|
||||
|
||||
public void addConnectionReceiver(ConnectionReceiver cr) {
|
||||
@@ -25,13 +25,13 @@ public abstract class BaseLinkProvider {
|
||||
}
|
||||
|
||||
//These two should be called when the provider links to a new computer
|
||||
protected void connectionAccepted(NetworkPackage identityPackage, BaseLink link) {
|
||||
protected void connectionAccepted(NetworkPackage identityPackage, BaseComputerLink link) {
|
||||
Log.i("LinkProvider", "connectionAccepted");
|
||||
for(ConnectionReceiver cr : connectionReceivers) {
|
||||
cr.onConnectionReceived(identityPackage, link);
|
||||
}
|
||||
}
|
||||
protected void connectionLost(BaseLink link) {
|
||||
protected void connectionLost(BaseComputerLink link) {
|
||||
Log.i("LinkProvider", "connectionLost");
|
||||
for(ConnectionReceiver cr : connectionReceivers) {
|
||||
cr.onConnectionLost(link);
|
@@ -1,9 +1,8 @@
|
||||
package org.kde.kdeconnect.Backends.LanBackend;
|
||||
package org.kde.kdeconnect.LinkProviders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import android.support.v4.util.LongSparseArray;
|
||||
|
||||
import org.apache.mina.core.future.ConnectFuture;
|
||||
import org.apache.mina.core.future.IoFuture;
|
||||
@@ -17,7 +16,7 @@ import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
|
||||
import org.apache.mina.transport.socket.nio.NioDatagramAcceptor;
|
||||
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
|
||||
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.ComputerLinks.LanComputerLink;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
@@ -31,9 +30,9 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
|
||||
private final static int port = 1714;
|
||||
|
||||
private final Context context;
|
||||
private final HashMap<String, LanLink> visibleComputers = new HashMap<String, LanLink>();
|
||||
private final LongSparseArray<LanLink> nioSessions = new LongSparseArray<LanLink>();
|
||||
private Context context;
|
||||
private HashMap<String, LanComputerLink> visibleComputers = new HashMap<String, LanComputerLink>();
|
||||
private HashMap<Long, LanComputerLink> nioSessions = new HashMap<Long, LanComputerLink>();
|
||||
|
||||
private NioSocketAcceptor tcpAcceptor = null;
|
||||
private NioDatagramAcceptor udpAcceptor = null;
|
||||
@@ -42,9 +41,8 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
@Override
|
||||
public void sessionClosed(IoSession session) throws Exception {
|
||||
|
||||
LanLink brokenLink = nioSessions.get(session.getId());
|
||||
LanComputerLink brokenLink = nioSessions.remove(session.getId());
|
||||
if (brokenLink != null) {
|
||||
nioSessions.remove(session.getId());
|
||||
connectionLost(brokenLink);
|
||||
brokenLink.disconnect();
|
||||
String deviceId = brokenLink.getDeviceId();
|
||||
@@ -60,30 +58,21 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
super.messageReceived(session, message);
|
||||
|
||||
//Log.e("LanLinkProvider","Incoming package, address: "+session.getRemoteAddress()).toString());
|
||||
//Log.e("LanLinkProvider","Received:"+message);
|
||||
|
||||
String theMessage = (String) message;
|
||||
if (theMessage.isEmpty()) {
|
||||
Log.e("LanLinkProvider","Empty package received");
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkPackage np = NetworkPackage.unserialize(theMessage);
|
||||
|
||||
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
|
||||
LanComputerLink prevLink = nioSessions.get(session.getId());
|
||||
|
||||
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
|
||||
String myId = NetworkPackage.createIdentityPackage(context).getString("deviceId");
|
||||
if (np.getString("deviceId").equals(myId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Log.e("LanLinkProvider", "Identity package received from "+np.getString("deviceName"));
|
||||
|
||||
LanLink link = new LanLink(session, np.getString("deviceId"), LanLinkProvider.this);
|
||||
LanComputerLink link = new LanComputerLink(session, np.getString("deviceId"), LanLinkProvider.this);
|
||||
nioSessions.put(session.getId(),link);
|
||||
addLink(np, link);
|
||||
} else {
|
||||
LanLink prevLink = nioSessions.get(session.getId());
|
||||
if (prevLink == null) {
|
||||
Log.e("LanLinkProvider","2 Expecting an identity package");
|
||||
} else {
|
||||
@@ -94,79 +83,84 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
}
|
||||
};
|
||||
|
||||
private final IoHandler udpHandler = new IoHandlerAdapter() {
|
||||
private IoHandler udpHandler = new IoHandlerAdapter() {
|
||||
@Override
|
||||
public void messageReceived(IoSession udpSession, Object message) throws Exception {
|
||||
super.messageReceived(udpSession, message);
|
||||
|
||||
//Log.e("LanLinkProvider", "Udp message received (" + message.getClass() + ") " + message.toString());
|
||||
|
||||
NetworkPackage np = null;
|
||||
|
||||
try {
|
||||
//We should receive a string thanks to the TextLineCodecFactory filter
|
||||
String theMessage = (String) message;
|
||||
final NetworkPackage identityPackage = NetworkPackage.unserialize(theMessage);
|
||||
np = NetworkPackage.unserialize(theMessage);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("LanLinkProvider", "Could not unserialize package");
|
||||
}
|
||||
|
||||
if (!identityPackage.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
|
||||
if (np != null) {
|
||||
|
||||
final NetworkPackage identityPackage = np;
|
||||
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
|
||||
Log.e("LanLinkProvider", "1 Expecting an identity package");
|
||||
return;
|
||||
} else {
|
||||
String myId = NetworkPackage.createIdentityPackage(context).getString("deviceId");
|
||||
if (identityPackage.getString("deviceId").equals(myId)) {
|
||||
if (np.getString("deviceId").equals(myId)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log.i("LanLinkProvider", "Identity package received, creating link");
|
||||
|
||||
final InetSocketAddress address = (InetSocketAddress) udpSession.getRemoteAddress();
|
||||
try {
|
||||
final InetSocketAddress address = (InetSocketAddress) udpSession.getRemoteAddress();
|
||||
|
||||
final NioSocketConnector connector = new NioSocketConnector();
|
||||
connector.setHandler(tcpHandler);
|
||||
//TextLineCodecFactory will split incoming data delimited by the given string
|
||||
connector.getFilterChain().addLast("codec",
|
||||
new ProtocolCodecFilter(
|
||||
new TextLineCodecFactory(Charset.defaultCharset(), LineDelimiter.UNIX, LineDelimiter.UNIX)
|
||||
)
|
||||
);
|
||||
connector.getSessionConfig().setKeepAlive(true);
|
||||
final NioSocketConnector connector = new NioSocketConnector();
|
||||
connector.setHandler(tcpHandler);
|
||||
//TextLineCodecFactory will split incoming data delimited by the given string
|
||||
connector.getFilterChain().addLast("codec",
|
||||
new ProtocolCodecFilter(
|
||||
new TextLineCodecFactory(Charset.defaultCharset(), LineDelimiter.UNIX, LineDelimiter.UNIX)
|
||||
)
|
||||
);
|
||||
connector.getSessionConfig().setKeepAlive(true);
|
||||
|
||||
int tcpPort = identityPackage.getInt("tcpPort",port);
|
||||
ConnectFuture future = connector.connect(new InetSocketAddress(address.getAddress(), tcpPort));
|
||||
future.addListener(new IoFutureListener<IoFuture>() {
|
||||
@Override
|
||||
public void operationComplete(IoFuture ioFuture) {
|
||||
final IoSession session = ioFuture.getSession();
|
||||
int tcpPort = np.getInt("tcpPort",port);
|
||||
ConnectFuture future = connector.connect(new InetSocketAddress(address.getAddress(), tcpPort));
|
||||
future.addListener(new IoFutureListener<IoFuture>() {
|
||||
@Override
|
||||
public void operationComplete(IoFuture ioFuture) {
|
||||
IoSession session = ioFuture.getSession();
|
||||
|
||||
final LanLink link = new LanLink(session, identityPackage.getString("deviceId"), LanLinkProvider.this);
|
||||
Log.i("LanLinkProvider", "Connection successful: " + session.isConnected());
|
||||
|
||||
Log.i("LanLinkProvider", "Connection successful: " + session.isConnected());
|
||||
LanComputerLink link = new LanComputerLink(session, identityPackage.getString("deviceId"), LanLinkProvider.this);
|
||||
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
NetworkPackage np2 = NetworkPackage.createIdentityPackage(context);
|
||||
link.sendPackage(np2);
|
||||
NetworkPackage np2 = NetworkPackage.createIdentityPackage(context);
|
||||
link.sendPackage(np2);
|
||||
|
||||
nioSessions.put(session.getId(), link);
|
||||
addLink(identityPackage, link);
|
||||
}
|
||||
}).start();
|
||||
nioSessions.put(session.getId(), link);
|
||||
addLink(identityPackage, link);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e("LanLinkProvider","Exception!!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("LanLinkProvider","Exception receiving udp package!!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
private void addLink(NetworkPackage identityPackage, LanLink link) {
|
||||
private void addLink(NetworkPackage identityPackage, LanComputerLink link) {
|
||||
String deviceId = identityPackage.getString("deviceId");
|
||||
Log.i("LanLinkProvider","addLink to "+deviceId);
|
||||
LanLink oldLink = visibleComputers.get(deviceId);
|
||||
LanComputerLink oldLink = visibleComputers.get(deviceId);
|
||||
visibleComputers.put(deviceId, link);
|
||||
connectionAccepted(identityPackage, link);
|
||||
if (oldLink != null) {
|
@@ -0,0 +1,65 @@
|
||||
package org.kde.kdeconnect.LinkProviders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.mina.core.future.ConnectFuture;
|
||||
import org.apache.mina.core.future.IoFuture;
|
||||
import org.apache.mina.core.future.IoFutureListener;
|
||||
import org.apache.mina.core.service.IoHandler;
|
||||
import org.apache.mina.core.service.IoHandlerAdapter;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolCodecFilter;
|
||||
import org.apache.mina.filter.codec.textline.LineDelimiter;
|
||||
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
|
||||
import org.apache.mina.transport.socket.nio.NioDatagramAcceptor;
|
||||
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
|
||||
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||
import org.kde.kdeconnect.ComputerLinks.LanComputerLink;
|
||||
import org.kde.kdeconnect.ComputerLinks.LoopbackComputerLink;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class LoopbackLinkProvider extends BaseLinkProvider {
|
||||
|
||||
private Context context;
|
||||
|
||||
public LoopbackLinkProvider(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
onNetworkChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkChange() {
|
||||
|
||||
NetworkPackage np = NetworkPackage.createIdentityPackage(context);
|
||||
connectionAccepted(np, new LoopbackComputerLink(this));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "LoopbackLinkProvider";
|
||||
}
|
||||
}
|
@@ -8,12 +8,9 @@ import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.UserInterface.MainSettingsActivity;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
@@ -23,40 +20,29 @@ import javax.crypto.Cipher;
|
||||
|
||||
public class NetworkPackage {
|
||||
|
||||
public final static int ProtocolVersion = 5;
|
||||
public final static int ProtocolVersion = 3;
|
||||
|
||||
//TODO: Move these to their respective plugins
|
||||
public final static String PACKAGE_TYPE_IDENTITY = "kdeconnect.identity";
|
||||
public final static String PACKAGE_TYPE_PAIR = "kdeconnect.pair";
|
||||
public final static String PACKAGE_TYPE_ENCRYPTED = "kdeconnect.encrypted";
|
||||
public final static String PACKAGE_TYPE_PING = "kdeconnect.ping";
|
||||
public final static String PACKAGE_TYPE_TELEPHONY = "kdeconnect.telephony";
|
||||
public final static String PACKAGE_TYPE_BATTERY = "kdeconnect.battery";
|
||||
public final static String PACKAGE_TYPE_SFTP = "kdeconnect.sftp";
|
||||
public final static String PACKAGE_TYPE_NOTIFICATION = "kdeconnect.notification";
|
||||
public final static String PACKAGE_TYPE_CLIPBOARD = "kdeconnect.clipboard";
|
||||
public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris";
|
||||
public final static String PACKAGE_TYPE_MOUSEPAD = "kdeconnect.mousepad";
|
||||
public final static String PACKAGE_TYPE_SHARE = "kdeconnect.share";
|
||||
|
||||
private long mId;
|
||||
private String mType;
|
||||
private JSONObject mBody;
|
||||
private InputStream mPayload;
|
||||
private JSONObject mPayloadTransferInfo;
|
||||
private int mPayloadSize;
|
||||
|
||||
private NetworkPackage() {
|
||||
|
||||
}
|
||||
|
||||
public NetworkPackage(String type) {
|
||||
mId = System.currentTimeMillis();
|
||||
mType = type;
|
||||
mBody = new JSONObject();
|
||||
mPayload = null;
|
||||
mPayloadSize = 0;
|
||||
mPayloadTransferInfo = new JSONObject();
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
@@ -76,9 +62,6 @@ public class NetworkPackage {
|
||||
public double getDouble(String key) { return mBody.optDouble(key,Double.NaN); }
|
||||
public double getDouble(String key, double defaultValue) { return mBody.optDouble(key,defaultValue); }
|
||||
public void set(String key, double value) { try { mBody.put(key,value); } catch(Exception e) { } }
|
||||
public JSONArray getJSONArray(String key) { return mBody.optJSONArray(key); }
|
||||
public void set(String key, JSONArray value) { try { mBody.put(key,value); } catch(Exception e) { } }
|
||||
|
||||
public ArrayList<String> getStringList(String key) {
|
||||
JSONArray jsonArray = mBody.optJSONArray(key);
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
@@ -108,64 +91,40 @@ public class NetworkPackage {
|
||||
|
||||
}
|
||||
}
|
||||
public boolean has(String key) { return mBody.has(key); }
|
||||
|
||||
public boolean isEncrypted() { return mType.equals(PACKAGE_TYPE_ENCRYPTED); }
|
||||
public boolean has(String key) { return mBody.has(key); }
|
||||
|
||||
public String serialize() {
|
||||
JSONObject jo = new JSONObject();
|
||||
try {
|
||||
jo.put("id", mId);
|
||||
jo.put("type", mType);
|
||||
jo.put("body", mBody);
|
||||
if (hasPayload()) {
|
||||
jo.put("payloadSize", mPayloadSize);
|
||||
jo.put("payloadTransferInfo", mPayloadTransferInfo);
|
||||
}
|
||||
jo.put("id",mId);
|
||||
jo.put("type",mType);
|
||||
jo.put("body",mBody);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("NetworkPackage", "Serialization exception");
|
||||
}
|
||||
|
||||
//QJSon does not escape slashes, but Java JSONObject does. Converting to QJson format.
|
||||
String json = jo.toString().replace("\\/","/")+"\n";
|
||||
|
||||
if (!isEncrypted()) {
|
||||
//Log.e("NetworkPackage.serialize", json);
|
||||
}
|
||||
|
||||
//Log.e("NetworkPackage.serialize",json);
|
||||
return json;
|
||||
}
|
||||
|
||||
static public NetworkPackage unserialize(String s) {
|
||||
|
||||
//Log.e("NetworkPackage.unserialize", s);
|
||||
NetworkPackage np = new NetworkPackage();
|
||||
try {
|
||||
JSONObject jo = new JSONObject(s);
|
||||
np.mId = jo.getLong("id");
|
||||
np.mType = jo.getString("type");
|
||||
np.mBody = jo.getJSONObject("body");
|
||||
if (jo.has("payloadSize")) {
|
||||
np.mPayloadTransferInfo = jo.getJSONObject("payloadTransferInfo");
|
||||
np.mPayloadSize = jo.getInt("payloadSize");
|
||||
} else {
|
||||
np.mPayloadTransferInfo = new JSONObject();
|
||||
np.mPayloadSize = 0;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("NetworkPackage", "Unserialization exception unserializing "+s);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!np.isEncrypted()) {
|
||||
//Log.e("NetworkPackage.unserialize", s);
|
||||
}
|
||||
|
||||
return np;
|
||||
}
|
||||
|
||||
public NetworkPackage encrypt(PublicKey publicKey) throws Exception {
|
||||
|
||||
|
||||
public void encrypt(PublicKey publicKey) throws Exception {
|
||||
|
||||
String serialized = serialize();
|
||||
|
||||
@@ -187,11 +146,17 @@ public class NetworkPackage {
|
||||
chunks.put(Base64.encodeToString(encryptedChunk, Base64.NO_WRAP));
|
||||
}
|
||||
|
||||
//Log.i("NetworkPackage", "Encrypted " + chunks.length()+" chunks");
|
||||
mId = System.currentTimeMillis();
|
||||
mType = NetworkPackage.PACKAGE_TYPE_ENCRYPTED;
|
||||
mBody = new JSONObject();
|
||||
try {
|
||||
mBody.put("data", chunks);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
Log.e("NetworkPackage","Exception");
|
||||
}
|
||||
|
||||
NetworkPackage encrypted = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_ENCRYPTED);
|
||||
encrypted.set("data", chunks);
|
||||
return encrypted;
|
||||
Log.i("NetworkPackage", "Encrypted " + chunks.length()+" chunks");
|
||||
|
||||
}
|
||||
|
||||
@@ -212,6 +177,7 @@ public class NetworkPackage {
|
||||
return unserialize(decryptedJson);
|
||||
}
|
||||
|
||||
|
||||
static public NetworkPackage createIdentityPackage(Context context) {
|
||||
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY);
|
||||
@@ -219,22 +185,15 @@ public class NetworkPackage {
|
||||
String deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||
try {
|
||||
np.mBody.put("deviceId", deviceId);
|
||||
np.mBody.put("deviceName",
|
||||
PreferenceManager.getDefaultSharedPreferences(context).getString(
|
||||
MainSettingsActivity.KEY_DEVICE_NAME_PREFERENCE,
|
||||
DeviceHelper.getDeviceName()));
|
||||
np.mBody.put("deviceName", HumanDeviceNames.getDeviceName());
|
||||
np.mBody.put("protocolVersion", NetworkPackage.ProtocolVersion);
|
||||
np.mBody.put("deviceType", DeviceHelper.isTablet()? "tablet" : "phone");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("NetworkPacakge","Exception on createIdentityPackage");
|
||||
} catch (JSONException e) {
|
||||
}
|
||||
|
||||
return np;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public NetworkPackage createPublicKeyPackage(Context context) {
|
||||
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
|
||||
@@ -249,40 +208,4 @@ public class NetworkPackage {
|
||||
|
||||
}
|
||||
|
||||
public void setPayload(byte[] data) {
|
||||
setPayload(new ByteArrayInputStream(data), data.length);
|
||||
}
|
||||
|
||||
public void setPayload(InputStream stream, int size) {
|
||||
mPayload = stream;
|
||||
mPayloadSize = size;
|
||||
}
|
||||
|
||||
/*public void setPayload(InputStream stream) {
|
||||
setPayload(stream, -1);
|
||||
}*/
|
||||
|
||||
public InputStream getPayload() {
|
||||
return mPayload;
|
||||
}
|
||||
|
||||
public int getPayloadSize() {
|
||||
return mPayloadSize;
|
||||
}
|
||||
|
||||
public boolean hasPayload() {
|
||||
return (mPayload != null);
|
||||
}
|
||||
|
||||
public boolean hasPayloadTransferInfo() {
|
||||
return (mPayloadTransferInfo.length() > 0);
|
||||
}
|
||||
|
||||
public JSONObject getPayloadTransferInfo() {
|
||||
return mPayloadTransferInfo;
|
||||
}
|
||||
|
||||
public void setPayloadTransferInfo(JSONObject payloadTransferInfo) {
|
||||
mPayloadTransferInfo = payloadTransferInfo;
|
||||
}
|
||||
}
|
@@ -8,6 +8,7 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.BatteryManager;
|
||||
import android.util.Log;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
@@ -16,11 +17,9 @@ import org.kde.kdeconnect_tp.R;
|
||||
|
||||
public class BatteryPlugin extends Plugin {
|
||||
|
||||
// keep these fields in sync with kdeconnect-kded:BatteryPlugin.h:ThresholdBatteryEvent
|
||||
private static final int THRESHOLD_EVENT_NONE= 0;
|
||||
private static final int THRESHOLD_EVENT_BATTERY_LOW = 1;
|
||||
private NetworkPackage lastPackage = null;
|
||||
|
||||
private NetworkPackage lastInfo = null;
|
||||
private IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
|
||||
|
||||
/*static {
|
||||
PluginFactory.registerPlugin(BatteryPlugin.class);
|
||||
@@ -51,36 +50,36 @@ public class BatteryPlugin extends Plugin {
|
||||
return true;
|
||||
}
|
||||
|
||||
private final BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||
|
||||
private BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent batteryIntent) {
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
Intent batteryChargeIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
int level = batteryChargeIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
|
||||
int scale = batteryChargeIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);
|
||||
int currentCharge = level*100 / scale;
|
||||
boolean isCharging = (0 != batteryChargeIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0));
|
||||
boolean lowBattery = Intent.ACTION_BATTERY_LOW.equals(batteryIntent.getAction());
|
||||
int thresholdEvent = lowBattery? THRESHOLD_EVENT_BATTERY_LOW : THRESHOLD_EVENT_NONE;
|
||||
Log.i("BatteryPlugin", "Battery event");
|
||||
|
||||
if (lastInfo != null
|
||||
&& isCharging != lastInfo.getBoolean("isCharging")
|
||||
&& currentCharge != lastInfo.getInt("currentCharge")
|
||||
&& thresholdEvent != lastInfo.getInt("thresholdEvent")
|
||||
boolean isCharging = (0 != intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0));
|
||||
|
||||
int currentCharge = 100;
|
||||
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
|
||||
if (status != BatteryManager.BATTERY_STATUS_FULL) {
|
||||
Intent batteryStatus = context.registerReceiver(null, filter);
|
||||
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
|
||||
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
|
||||
currentCharge = level*100 / scale;
|
||||
}
|
||||
|
||||
//Only notify if change is meaningful enough
|
||||
if (lastPackage == null
|
||||
|| (
|
||||
isCharging != lastPackage.getBoolean("isCharging")
|
||||
|| currentCharge != lastPackage.getInt("currentCharge")
|
||||
)
|
||||
) {
|
||||
|
||||
//Do not send again if nothing has changed
|
||||
return;
|
||||
|
||||
} else {
|
||||
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_BATTERY);
|
||||
np.set("currentCharge", currentCharge);
|
||||
np.set("isCharging", isCharging);
|
||||
np.set("thresholdEvent", thresholdEvent);
|
||||
np.set("currentCharge", currentCharge);
|
||||
device.sendPackage(np);
|
||||
lastInfo = np;
|
||||
|
||||
lastPackage = np;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -88,14 +87,12 @@ public class BatteryPlugin extends Plugin {
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_BATTERY_LOW));
|
||||
context.registerReceiver(receiver, filter);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
//It's okay to call this only once, even though we registered it for two filters
|
||||
context.unregisterReceiver(receiver);
|
||||
}
|
||||
|
||||
@@ -104,8 +101,8 @@ public class BatteryPlugin extends Plugin {
|
||||
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_BATTERY)) return false;
|
||||
|
||||
if (np.getBoolean("request")) {
|
||||
if (lastInfo != null) {
|
||||
device.sendPackage(lastInfo);
|
||||
if (lastPackage != null) {
|
||||
device.sendPackage(lastPackage);
|
||||
}
|
||||
}
|
||||
|
@@ -10,18 +10,12 @@ import org.kde.kdeconnect.NetworkPackage;
|
||||
public class ClipboardListener {
|
||||
|
||||
|
||||
private final Context context;
|
||||
private String currentContent;
|
||||
|
||||
private ClipboardManager cm = null;
|
||||
private ClipboardManager.OnPrimaryClipChangedListener listener;
|
||||
|
||||
ClipboardListener(final Context ctx, final Device device) {
|
||||
context = ctx;
|
||||
if(android.os.Build.VERSION.SDK_INT < 11) {
|
||||
return;
|
||||
}
|
||||
ClipboardManager.OnPrimaryClipChangedListener listener;
|
||||
|
||||
ClipboardListener(final Context context, final Device device) {
|
||||
cm = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
listener = new ClipboardManager.OnPrimaryClipChangedListener() {
|
||||
@Override
|
||||
@@ -47,24 +41,12 @@ public class ClipboardListener {
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if(android.os.Build.VERSION.SDK_INT < 11) {
|
||||
return;
|
||||
}
|
||||
|
||||
cm.removePrimaryClipChangedListener(listener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setText(String text) {
|
||||
currentContent = text;
|
||||
if(android.os.Build.VERSION.SDK_INT < 11) {
|
||||
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
clipboard.setText(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
cm.setText(text);
|
||||
}
|
||||
cm.setText(text);
|
||||
}
|
||||
|
||||
}
|
@@ -3,7 +3,9 @@ package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
@@ -37,21 +39,31 @@ public class ClipboardPlugin extends Plugin {
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {
|
||||
//Disabled by default due to just one direction sync(incoming clipboard change) in early version of android.
|
||||
return (android.os.Build.VERSION.SDK_INT >= 11);
|
||||
return (Build.VERSION.SDK_INT >= 11);
|
||||
}
|
||||
|
||||
private ClipboardListener listener;
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
|
||||
if (Build.VERSION.SDK_INT < 11) {
|
||||
return false;
|
||||
}
|
||||
|
||||
listener = new ClipboardListener(context, device);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
|
||||
if (Build.VERSION.SDK_INT < 11) return;
|
||||
|
||||
listener.stop();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,10 +76,22 @@ public class ClipboardPlugin extends Plugin {
|
||||
String content = np.getString("content");
|
||||
listener.setText(content);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog getErrorDialog(Context baseContext) { return null; }
|
||||
public AlertDialog getErrorDialog(Context baseContext) {
|
||||
return new AlertDialog.Builder(baseContext)
|
||||
.setTitle(R.string.pref_plugin_clipboard)
|
||||
.setMessage(R.string.plugin_not_available)
|
||||
.setPositiveButton("Ok",new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
|
||||
}
|
||||
})
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Button getInterfaceButton(Activity activity) {
|
@@ -5,7 +5,6 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
@@ -14,10 +13,10 @@ import android.widget.SeekBar;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.ComputerLinks.BaseComputerLink;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
@@ -25,11 +24,9 @@ import java.util.ArrayList;
|
||||
|
||||
public class MprisActivity extends Activity {
|
||||
|
||||
//TODO: Add a loading spinner at the beginning (to distinguish the loading state from a no-players state).
|
||||
//TODO: Add a loading spinner at the begginning (to distinguish the loading state from a no-players state).
|
||||
//TODO 2: Add a message when no players are detected after loading completes
|
||||
|
||||
private String deviceId;
|
||||
|
||||
protected void connectToPlugin() {
|
||||
|
||||
final String deviceId = getIntent().getStringExtra("deviceId");
|
||||
@@ -96,12 +93,8 @@ public class MprisActivity extends Activity {
|
||||
//Spotify doesn't support changing the volume yet...
|
||||
if (player.equals("Spotify")) {
|
||||
findViewById(R.id.volume_layout).setVisibility(View.INVISIBLE);
|
||||
findViewById(R.id.rew_button).setVisibility(View.GONE);
|
||||
findViewById(R.id.ff_button).setVisibility(View.GONE);
|
||||
} else {
|
||||
findViewById(R.id.volume_layout).setVisibility(View.VISIBLE);
|
||||
findViewById(R.id.rew_button).setVisibility(View.VISIBLE);
|
||||
findViewById(R.id.ff_button).setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,14 +119,14 @@ public class MprisActivity extends Activity {
|
||||
|
||||
}
|
||||
|
||||
private final BaseLinkProvider.ConnectionReceiver connectionReceiver = new BaseLinkProvider.ConnectionReceiver() {
|
||||
BaseLinkProvider.ConnectionReceiver connectionReceiver = new BaseLinkProvider.ConnectionReceiver() {
|
||||
@Override
|
||||
public void onConnectionReceived(NetworkPackage identityPackage, BaseLink link) {
|
||||
public void onConnectionReceived(NetworkPackage identityPackage, BaseComputerLink link) {
|
||||
connectToPlugin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost(BaseLink link) {
|
||||
public void onConnectionLost(BaseComputerLink link) {
|
||||
|
||||
}
|
||||
};
|
||||
@@ -149,73 +142,12 @@ public class MprisActivity extends Activity {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change current volume with provided step.
|
||||
*
|
||||
* @param mpris multimedia controller
|
||||
* @param step step size volume change
|
||||
*/
|
||||
private void updateVolume(MprisPlugin mpris, int step) {
|
||||
final int currentVolume = mpris.getVolume();
|
||||
if(currentVolume < 100 || currentVolume > 0) {
|
||||
int newVolume = currentVolume + step;
|
||||
if(newVolume > 100) {
|
||||
newVolume = 100;
|
||||
} else if (newVolume <0 ) {
|
||||
newVolume = 0;
|
||||
}
|
||||
mpris.setVolume(newVolume);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||
BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(deviceId);
|
||||
MprisPlugin mpris = (MprisPlugin) device.getPlugin("plugin_mpris");
|
||||
if (mpris == null) return;
|
||||
updateVolume(mpris, 5);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||
BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(deviceId);
|
||||
MprisPlugin mpris = (MprisPlugin) device.getPlugin("plugin_mpris");
|
||||
if (mpris == null) return;
|
||||
updateVolume(mpris, -5);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
default:
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||
return true;
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||
return true;
|
||||
default:
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.mpris_control);
|
||||
|
||||
deviceId = getIntent().getStringExtra("deviceId");
|
||||
final String deviceId = getIntent().getStringExtra("deviceId");
|
||||
|
||||
BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
@@ -255,36 +187,6 @@ public class MprisActivity extends Activity {
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.rew_button).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(deviceId);
|
||||
MprisPlugin mpris = (MprisPlugin)device.getPlugin("plugin_mpris");
|
||||
if (mpris == null) return;
|
||||
mpris.Seek(-10000000); // -10 seconds. TODO: plugin settings UI?
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.ff_button).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(deviceId);
|
||||
MprisPlugin mpris = (MprisPlugin)device.getPlugin("plugin_mpris");
|
||||
if (mpris == null) return;
|
||||
mpris.Seek(10000000); // 10 seconds. TODO: plugin settings UI?
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.next_button).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
@@ -302,12 +204,10 @@ public class MprisActivity extends Activity {
|
||||
|
||||
((SeekBar)findViewById(R.id.volume_seek)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
|
||||
}
|
||||
public void onProgressChanged(SeekBar seekBar, int i, boolean b) { }
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
public void onStartTrackingTouch(SeekBar seekBar) { }
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(final SeekBar seekBar) {
|
||||
@@ -319,14 +219,12 @@ public class MprisActivity extends Activity {
|
||||
if (mpris == null) return;
|
||||
mpris.setVolume(seekBar.getProgress());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -84,13 +84,6 @@ public class MprisPlugin extends Plugin {
|
||||
device.sendPackage(np);
|
||||
}
|
||||
|
||||
public void Seek(int offset) {
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MPRIS);
|
||||
np.set("player",player);
|
||||
np.set("Seek",offset);
|
||||
device.sendPackage(np);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPackageReceived(NetworkPackage np) {
|
||||
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_MPRIS)) return false;
|
@@ -5,6 +5,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -15,7 +16,7 @@ public class NotificationReceiver extends NotificationListenerService {
|
||||
void onNotificationRemoved(StatusBarNotification statusBarNotification);
|
||||
}
|
||||
|
||||
private final ArrayList<NotificationListener> listeners = new ArrayList<NotificationListener>();
|
||||
private ArrayList<NotificationListener> listeners = new ArrayList<NotificationListener>();
|
||||
|
||||
public void addListener(NotificationListener listener) {
|
||||
listeners.add(listener);
|
||||
@@ -26,7 +27,7 @@ public class NotificationReceiver extends NotificationListenerService {
|
||||
|
||||
@Override
|
||||
public void onNotificationPosted(StatusBarNotification statusBarNotification) {
|
||||
//Log.e("NotificationReceiver.onNotificationPosted","listeners: " + listeners.size());
|
||||
Log.i("NotificationReceiver.onNotificationPosted","listeners: " + listeners.size());
|
||||
for(NotificationListener listener : listeners) {
|
||||
listener.onNotificationPosted(statusBarNotification);
|
||||
}
|
||||
@@ -48,7 +49,7 @@ public class NotificationReceiver extends NotificationListenerService {
|
||||
//This will be called for each intent launch, even if the service is already started and is reused
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
//Log.e("NotificationReceiver", "onStartCommand");
|
||||
Log.i("NotificationReceiver", "onStartCommand");
|
||||
for (InstanceCallback c : callbacks) {
|
||||
c.onServiceStart(this);
|
||||
}
|
||||
@@ -60,7 +61,7 @@ public class NotificationReceiver extends NotificationListenerService {
|
||||
void onServiceStart(NotificationReceiver service);
|
||||
}
|
||||
|
||||
private final static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
|
||||
private static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
|
||||
|
||||
public static void Start(Context c) {
|
||||
RunCommand(c, null);
|
@@ -6,19 +6,25 @@ import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.kde.kdeconnect.Helpers.AppsHelper;
|
||||
import org.kde.kdeconnect.Helpers.ImagesHelper;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class NotificationsPlugin extends Plugin implements NotificationReceiver.NotificationListener {
|
||||
|
||||
/*static {
|
||||
@@ -110,9 +116,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
return false;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT < 18) return false;
|
||||
|
||||
//Check for permissions
|
||||
String notificationListenerList = Settings.Secure.getString(context.getContentResolver(), "enabled_notification_listeners");
|
||||
@@ -122,10 +126,12 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
||||
public void onServiceStart(NotificationReceiver service) {
|
||||
try {
|
||||
service.addListener(NotificationsPlugin.this);
|
||||
/*
|
||||
StatusBarNotification[] notifications = service.getActiveNotifications();
|
||||
for (StatusBarNotification notification : notifications) {
|
||||
sendNotification(notification, true);
|
||||
onNotificationPosted(notification);
|
||||
}
|
||||
*/
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("NotificationsPlugin","Exception");
|
||||
@@ -142,11 +148,6 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
return;
|
||||
}
|
||||
|
||||
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(NotificationReceiver service) {
|
||||
@@ -171,19 +172,12 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
||||
|
||||
@Override
|
||||
public void onNotificationPosted(StatusBarNotification statusBarNotification) {
|
||||
sendNotification(statusBarNotification, false);
|
||||
onNotificationPosted(statusBarNotification, false);
|
||||
}
|
||||
|
||||
public void sendNotification(StatusBarNotification statusBarNotification, boolean requestAnswer) {
|
||||
public void onNotificationPosted(StatusBarNotification statusBarNotification, boolean requestAnswer) {
|
||||
|
||||
Notification notification = statusBarNotification.getNotification();
|
||||
|
||||
if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0
|
||||
|| (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0 ) {
|
||||
//This is not a notification!
|
||||
return;
|
||||
}
|
||||
|
||||
NotificationId id = NotificationId.fromNotification(statusBarNotification);
|
||||
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_NOTIFICATION);
|
||||
@@ -191,26 +185,25 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
||||
String packageName = statusBarNotification.getPackageName();
|
||||
String appName = AppsHelper.appNameLookup(context, packageName);
|
||||
|
||||
//TODO: Add support for displaying app icons to desktop plasmoid and uncomment this piece of code
|
||||
/*
|
||||
try {
|
||||
//TODO: Scale down app icon if too big and compress as JPG
|
||||
Drawable drawableAppIcon = AppsHelper.appIconLookup(context, packageName);
|
||||
Bitmap appIcon = ImagesHelper.drawableToBitmap(drawableAppIcon);
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||
appIcon.compress(Bitmap.CompressFormat.PNG, 90, outStream);
|
||||
byte[] bitmapData = outStream.toByteArray();
|
||||
np.setPayload(bitmapData);
|
||||
byte[] serializedBitmapData = Base64.encode(bitmapData, Base64.NO_WRAP);
|
||||
String stringBitmapData = new String(serializedBitmapData, Charset.defaultCharset());
|
||||
//The icon is super big, better sending it as a file transfer when we support that
|
||||
//np.set("base64icon", stringBitmapData);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("NotificationsPlugin","Error retrieving icon");
|
||||
}
|
||||
*/
|
||||
|
||||
np.set("id", id.serialize());
|
||||
np.set("appName", appName == null? packageName : appName);
|
||||
np.set("isClearable", statusBarNotification.isClearable());
|
||||
np.set("ticker", getTickerText(notification));
|
||||
np.set("ticker", notification.tickerText.toString());
|
||||
np.set("time", Long.toString(statusBarNotification.getPostTime()));
|
||||
if (requestAnswer) np.set("requestAnswer", true);
|
||||
|
||||
@@ -218,43 +211,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ticker text of the notification.
|
||||
* If device android version is KitKat or newer, the title and text of the notification is used
|
||||
* instead the ticker text.
|
||||
*/
|
||||
private String getTickerText(Notification notification) {
|
||||
final String TITLE_KEY = "android.title";
|
||||
final String TEXT_KEY = "android.text";
|
||||
String ticker = "";
|
||||
|
||||
if(notification != null) {
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
try {
|
||||
Bundle extras = notification.extras;
|
||||
String extraTitle = extras.getString(TITLE_KEY);
|
||||
String extraText = extras.getString(TEXT_KEY);
|
||||
|
||||
if (extraTitle != null && extraText != null) {
|
||||
ticker = extraTitle + " ‐ " + extraText;
|
||||
} else if (extraTitle != null) {
|
||||
ticker = extraTitle;
|
||||
} else if (extraText != null) {
|
||||
ticker = extraText;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
Log.w("NotificationPlugin","problem parsing notification extras");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (ticker.isEmpty()) {
|
||||
ticker = (notification.tickerText != null)? notification.tickerText.toString() : "";
|
||||
}
|
||||
}
|
||||
|
||||
return ticker;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@@ -267,7 +224,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
||||
private void sendCurrentNotifications(NotificationReceiver service) {
|
||||
StatusBarNotification[] notifications = service.getActiveNotifications();
|
||||
for (StatusBarNotification notification : notifications) {
|
||||
sendNotification(notification, true);
|
||||
onNotificationPosted(notification, true);
|
||||
}
|
||||
}
|
||||
|
@@ -4,18 +4,14 @@ import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.UserInterface.MainActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
|
||||
@@ -67,18 +63,9 @@ public class PingPlugin extends Plugin {
|
||||
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_PING)) {
|
||||
//Log.e("PingPackageReceiver", "was a ping!");
|
||||
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
|
||||
stackBuilder.addParentStack(MainActivity.class);
|
||||
stackBuilder.addNextIntent(new Intent(context, MainActivity.class));
|
||||
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
|
||||
0,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
|
||||
Notification noti = new NotificationCompat.Builder(context)
|
||||
.setContentTitle(device.getName())
|
||||
.setContentText("Ping!")
|
||||
.setContentIntent(resultPendingIntent)
|
||||
.setTicker("Ping!")
|
||||
.setSmallIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setAutoCancel(true)
|
@@ -7,13 +7,10 @@ import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Plugins.BatteryPlugin.BatteryPlugin;
|
||||
import org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadPlugin;
|
||||
import org.kde.kdeconnect.Plugins.SftpPlugin.SftpPlugin;
|
||||
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardPlugin;
|
||||
import org.kde.kdeconnect.Plugins.MprisPlugin.MprisPlugin;
|
||||
import org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationsPlugin;
|
||||
import org.kde.kdeconnect.Plugins.PingPlugin.PingPlugin;
|
||||
import org.kde.kdeconnect.Plugins.SharePlugin.SharePlugin;
|
||||
import org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -52,11 +49,11 @@ public class PluginFactory {
|
||||
return enabledByDefault;
|
||||
}
|
||||
|
||||
private final String pluginName;
|
||||
private final String displayName;
|
||||
private final String description;
|
||||
private String pluginName;
|
||||
private String displayName;
|
||||
private String description;
|
||||
private final Drawable icon;
|
||||
private final boolean enabledByDefault;
|
||||
private boolean enabledByDefault;
|
||||
|
||||
}
|
||||
|
||||
@@ -69,11 +66,8 @@ public class PluginFactory {
|
||||
PluginFactory.registerPlugin(PingPlugin.class);
|
||||
PluginFactory.registerPlugin(MprisPlugin.class);
|
||||
PluginFactory.registerPlugin(ClipboardPlugin.class);
|
||||
PluginFactory.registerPlugin(BatteryPlugin.class);
|
||||
PluginFactory.registerPlugin(SftpPlugin.class);
|
||||
//PluginFactory.registerPlugin(BatteryPlugin.class);
|
||||
PluginFactory.registerPlugin(NotificationsPlugin.class);
|
||||
PluginFactory.registerPlugin(MousePadPlugin.class);
|
||||
PluginFactory.registerPlugin(SharePlugin.class);
|
||||
}
|
||||
|
||||
public static PluginInfo getPluginInfo(Context context, String pluginName) {
|
||||
@@ -115,10 +109,10 @@ public class PluginFactory {
|
||||
|
||||
}
|
||||
|
||||
public static void registerPlugin(Class<? extends Plugin> pluginClass) {
|
||||
public static void registerPlugin(Class pluginClass) {
|
||||
try {
|
||||
//I hate this but I need to create an instance because abstract static functions can't be declared
|
||||
String pluginName = (pluginClass.newInstance()).getPluginName();
|
||||
String pluginName = ((Plugin)pluginClass.newInstance()).getPluginName();
|
||||
availablePlugins.put(pluginName, pluginClass);
|
||||
} catch(Exception e) {
|
||||
Log.e("PluginFactory","addPlugin exception");
|
@@ -10,6 +10,7 @@ import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.kde.kdeconnect.Helpers.ContactsHelper;
|
||||
@@ -48,7 +49,7 @@ public class TelephonyPlugin extends Plugin {
|
||||
return true;
|
||||
}
|
||||
|
||||
private final BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||
private BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
@@ -95,8 +96,6 @@ public class TelephonyPlugin extends Plugin {
|
||||
|
||||
public void callBroadcastReceived(int state, String phoneNumber) {
|
||||
|
||||
//Log.e("TelephonyPlugin", "callBroadcastReceived");
|
||||
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_TELEPHONY);
|
||||
if (phoneNumber != null) {
|
||||
phoneNumber = ContactsHelper.phoneNumberLookup(context,phoneNumber);
|
||||
@@ -163,7 +162,6 @@ public class TelephonyPlugin extends Plugin {
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
//Log.e("TelephonyPlugin", "onCreate");
|
||||
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
|
||||
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
|
||||
context.registerReceiver(receiver, filter);
|
@@ -0,0 +1,148 @@
|
||||
package org.kde.kdeconnect.UserInterface;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.UserInterface.List.ButtonItem;
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||
import org.kde.kdeconnect.UserInterface.List.SectionItem;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class DeviceActivity extends ActionBarActivity {
|
||||
|
||||
private String deviceId;
|
||||
private Device device;
|
||||
|
||||
private Device.PluginsChangedListener pluginsChangedListener = new Device.PluginsChangedListener() {
|
||||
@Override
|
||||
public void onPluginsChanged(final Device device) {
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
//Errors list
|
||||
final HashMap<String, Plugin> failedPlugins = device.getFailedPlugins();
|
||||
final String[] ids = failedPlugins.keySet().toArray(new String[failedPlugins.size()]);
|
||||
String[] names = new String[failedPlugins.size()];
|
||||
for(int i = 0; i < ids.length; i++) {
|
||||
Plugin p = failedPlugins.get(ids[i]);
|
||||
names[i] = p.getDisplayName();
|
||||
}
|
||||
ListView errorList = (ListView)findViewById(R.id.errors_list);
|
||||
if (!failedPlugins.isEmpty() && errorList.getHeaderViewsCount() == 0) {
|
||||
TextView header = new TextView(DeviceActivity.this);
|
||||
header.setPadding(0,24,0,0);
|
||||
header.setText(getResources().getString(R.string.plugins_failed_to_load));
|
||||
errorList.addHeaderView(header);
|
||||
}
|
||||
errorList.setAdapter(new ArrayAdapter<String>(DeviceActivity.this, android.R.layout.simple_list_item_1, names));
|
||||
errorList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
||||
Plugin p = failedPlugins.get(ids[position - 1]); //Header is position 0, so we have to substract one
|
||||
p.getErrorDialog(DeviceActivity.this).show();
|
||||
}
|
||||
});
|
||||
|
||||
//Buttons list
|
||||
ArrayList<ListAdapter.Item> items = new ArrayList<ListAdapter.Item>();
|
||||
final Collection<Plugin> plugins = device.getLoadedPlugins().values();
|
||||
for (Plugin p : plugins) {
|
||||
Button b = p.getInterfaceButton(DeviceActivity.this);
|
||||
if (b != null) {
|
||||
items.add(new SectionItem(p.getDisplayName()));
|
||||
items.add(new ButtonItem(b));
|
||||
}
|
||||
}
|
||||
ListView buttonsList = (ListView)findViewById(R.id.buttons_list);
|
||||
buttonsList.setAdapter(new ListAdapter(DeviceActivity.this, items));
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_device);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
deviceId = getIntent().getStringExtra("deviceId");
|
||||
|
||||
BackgroundService.RunCommand(DeviceActivity.this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
device = service.getDevice(deviceId);
|
||||
setTitle(device.getName());
|
||||
device.addPluginsChangedListener(pluginsChangedListener);
|
||||
pluginsChangedListener.onPluginsChanged(device);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
BackgroundService.RunCommand(DeviceActivity.this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(deviceId);
|
||||
device.removePluginsChangedListener(pluginsChangedListener);
|
||||
}
|
||||
});
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
menu.clear();
|
||||
if (device.isPaired()) {
|
||||
menu.add(R.string.device_menu_plugins).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
Intent intent = new Intent(DeviceActivity.this, SettingsActivity.class);
|
||||
intent.putExtra("deviceId", deviceId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
menu.add(R.string.device_menu_unpair).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
device.unpair();
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -28,17 +28,6 @@ public class DeviceItem implements ListAdapter.Item {
|
||||
|
||||
TextView titleView = (TextView)v.findViewById(R.id.list_item_entry_title);
|
||||
if (titleView != null) titleView.setText(device.getName());
|
||||
if (device.compareProtocolVersion() != 0) {
|
||||
TextView summaryView = (TextView)v.findViewById(R.id.list_item_entry_summary);
|
||||
summaryView.setVisibility(View.VISIBLE);
|
||||
if (device.compareProtocolVersion() > 0) {
|
||||
summaryView.setText(R.string.protocol_version_newer);
|
||||
} else {
|
||||
summaryView.setText(R.string.protocol_version_older);
|
||||
}
|
||||
} else {
|
||||
v.findViewById(R.id.list_item_entry_summary).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
v.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
@@ -14,8 +14,8 @@ public class ListAdapter extends ArrayAdapter<ListAdapter.Item> {
|
||||
public View inflateView(LayoutInflater layoutInflater);
|
||||
}
|
||||
|
||||
private final ArrayList<Item> items;
|
||||
private final LayoutInflater layoutInflater;
|
||||
private ArrayList<Item> items;
|
||||
private LayoutInflater layoutInflater;
|
||||
|
||||
public ListAdapter(Context context, ArrayList<Item> items) {
|
||||
super(context, 0, items);
|
@@ -13,7 +13,6 @@ public class SectionItem implements ListAdapter.Item {
|
||||
|
||||
public SectionItem(String title) {
|
||||
this.title = title;
|
||||
this.isEmpty = false;
|
||||
}
|
||||
|
||||
@Override
|
@@ -1,11 +1,11 @@
|
||||
package org.kde.kdeconnect.UserInterface;
|
||||
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
@@ -66,9 +66,6 @@ public class MainActivity extends ActionBarActivity {
|
||||
}
|
||||
}).start();
|
||||
break;
|
||||
case R.id.menu_settings:
|
||||
startActivity(new Intent(this,MainSettingsActivity.class));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -82,6 +79,7 @@ public class MainActivity extends ActionBarActivity {
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -144,13 +142,12 @@ public class MainActivity extends ActionBarActivity {
|
||||
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
view.performClick();
|
||||
view.callOnClick();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
}
|
@@ -18,7 +18,7 @@ public class PairActivity extends ActionBarActivity {
|
||||
private String deviceId;
|
||||
private Device device = null;
|
||||
|
||||
private final Device.PairingCallback pairingCallback = new Device.PairingCallback() {
|
||||
private Device.PairingCallback pairingCallback = new Device.PairingCallback() {
|
||||
|
||||
@Override
|
||||
public void incomingRequest() {
|
@@ -2,6 +2,7 @@ package org.kde.kdeconnect.UserInterface;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.Preference;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
@@ -11,7 +12,7 @@ import java.util.ArrayList;
|
||||
public class PreferenceListAdapter extends ArrayAdapter<Preference> {
|
||||
|
||||
|
||||
private final ArrayList<Preference> localList;
|
||||
private ArrayList<Preference> localList;
|
||||
|
||||
public PreferenceListAdapter(Context context, ArrayList<Preference> items) {
|
||||
super(context,0, items);
|
@@ -4,6 +4,7 @@ import android.app.ListActivity;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.Preference;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
|
BIN
KdeConnect/src/main/res/drawable/action_settings.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
KdeConnect/src/main/res/drawable/icon.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
KdeConnect/src/main/res/drawable/navigation_refresh.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
KdeConnect/src/main/res/drawable/volume.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
48
KdeConnect/src/main/res/layout/list_item_entry.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:baselineAligned="false"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingRight="?android:attr/scrollbarSize">
|
||||
<!--
|
||||
<ImageView
|
||||
android:id="@+id/list_item_entry_drawable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:src="@android:drawable/ic_menu_preferences"
|
||||
android:paddingLeft="9dp"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dip"
|
||||
android:layout_marginRight="6dip"
|
||||
android:layout_marginTop="3dip"
|
||||
android:layout_marginBottom="3dip"
|
||||
android:layout_weight="0">
|
||||
-->
|
||||
<TextView android:id="@+id/list_item_entry_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal" />
|
||||
|
||||
<!--
|
||||
<TextView android:id="@+id/list_item_entry_summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/list_item_entry_title"
|
||||
android:layout_alignLeft="@id/list_item_entry_title"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:singleLine="true"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
-->
|
||||
</LinearLayout>
|
@@ -37,7 +37,7 @@
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="200dip"
|
||||
android:layout_height="50dip"
|
||||
android:layout_height="70dip"
|
||||
android:layout_gravity="center"
|
||||
>
|
||||
|
||||
@@ -45,36 +45,19 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:id="@+id/prev_button"
|
||||
android:src="@android:drawable/ic_media_previous"
|
||||
android:contentDescription="@string/mpris_previous"
|
||||
android:layout_weight="0.25"
|
||||
/>
|
||||
|
||||
<ImageButton
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:id="@+id/rew_button"
|
||||
android:src="@android:drawable/ic_media_rew"
|
||||
android:contentDescription="@string/mpris_rew"
|
||||
android:layout_weight="0.25"
|
||||
/>
|
||||
|
||||
<ImageButton
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:id="@+id/ff_button"
|
||||
android:src="@android:drawable/ic_media_ff"
|
||||
android:contentDescription="@string/mpris_ff"
|
||||
android:layout_weight="0.25"
|
||||
android:contentDescription="@string/mpris_previous"
|
||||
android:layout_weight="0.5"
|
||||
/>
|
||||
|
||||
<ImageButton
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:id="@+id/next_button"
|
||||
android:src="@android:drawable/ic_media_next"
|
||||
android:src="@android:drawable/ic_media_ff"
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/mpris_next"
|
||||
android:layout_weight="0.25"
|
||||
android:layout_weight="0.5"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -94,7 +77,7 @@
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="left|center_vertical"
|
||||
android:contentDescription="@string/mpris_volume"
|
||||
android:src="@drawable/ic_volume"
|
||||
android:src="@drawable/volume"
|
||||
/>
|
||||
|
||||
|
21
KdeConnect/src/main/res/menu/main.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:kdeconnect="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_refresh"
|
||||
android:icon="@drawable/navigation_refresh"
|
||||
android:orderInCategory="200"
|
||||
kdeconnect:showAsAction="always"
|
||||
android:title="@string/reconnect"
|
||||
/>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_progress"
|
||||
android:icon="@drawable/navigation_refresh"
|
||||
android:orderInCategory="200"
|
||||
android:visible="false"
|
||||
kdeconnect:showAsAction="always"
|
||||
kdeconnect:actionViewClass="android.widget.ProgressBar"
|
||||
/>
|
||||
|
||||
</menu>
|
@@ -5,20 +5,14 @@
|
||||
<string name="pref_plugin_telephony_desc">Send notifications for SMS and calls</string>
|
||||
<string name="pref_plugin_battery">Battery report</string>
|
||||
<string name="pref_plugin_battery_desc">Periodically report battery status</string>
|
||||
<string name="pref_plugin_sftp">Filesystem expose</string>
|
||||
<string name="pref_plugin_sftp_desc">Allows to browse the phone\'s filesystem remotely</string>
|
||||
<string name="pref_plugin_clipboard">Clipboard sync</string>
|
||||
<string name="pref_plugin_clipboard_desc">Share the clipboard content</string>
|
||||
<string name="pref_plugin_mousepad">Mouse Remote Control</string>
|
||||
<string name="pref_plugin_mousepad_desc">Control your mouse remotely</string>
|
||||
<string name="pref_plugin_mpris">Multimedia remote controls</string>
|
||||
<string name="pref_plugin_mpris_desc">Control audio/video from your phone</string>
|
||||
<string name="pref_plugin_ping">Ping</string>
|
||||
<string name="pref_plugin_ping_desc">Send and receive pings</string>
|
||||
<string name="pref_plugin_notifications">Notification sync</string>
|
||||
<string name="pref_plugin_notifications_desc">Access your notifications from other devices</string>
|
||||
<string name="pref_plugin_sharereceiver">Share receiver</string>
|
||||
<string name="pref_plugin_sharereceiver_desc">Save incoming files to the phone storage</string>
|
||||
<string name="plugin_not_available">This feature is not available in your Android version</string>
|
||||
<string name="device_list_empty">No devices</string>
|
||||
<string name="ok">OK</string>
|
||||
@@ -27,15 +21,12 @@
|
||||
<string name="no_permissions">You need to grant permission to access notifications</string>
|
||||
<string name="send_ping">Send ping</string>
|
||||
<string name="open_mpris_controls">Open remote control</string>
|
||||
<string name="open_mousepad">Open touchpad control</string>
|
||||
<string name="mousepad_info">Move a finger on the screen to move the mouse cursor</string>
|
||||
<string name="category_connected_devices">Connected devices</string>
|
||||
<string name="category_not_paired_devices">Not paired devices</string>
|
||||
<string name="category_remembered_devices">Remembered devices</string>
|
||||
<string name="plugins_failed_to_load">Plugins failed to load (tap for more info):</string>
|
||||
<string name="device_menu_plugins">Select plugins</string>
|
||||
<string name="device_menu_unpair">Unpair</string>
|
||||
<string name="device_not_reachable">Paired device not reachable</string>
|
||||
<string name="unknown_device">Unknown device</string>
|
||||
<string name="error_not_reachable">Device not reachable</string>
|
||||
<string name="error_already_requested">Pairing already requested</string>
|
||||
@@ -47,16 +38,8 @@
|
||||
<string name="error_invalid_key">Invalid key received</string>
|
||||
<string name="pair_requested">Pair requested</string>
|
||||
<string name="pairing_request_from">Pairing request from %1s</string>
|
||||
<string name="received_url_title">Received link from %1s</string>
|
||||
<string name="received_url_text">Tap to open \'%1s\'</string>
|
||||
<string name="incoming_file_title">Incoming file from %1s</string>
|
||||
<string name="incoming_file_text">%1s</string>
|
||||
<string name="received_file_title">Received file from %1s</string>
|
||||
<string name="received_file_text">Tap to open \'%1s\'</string>
|
||||
<string name="tap_to_answer">Tap to answer</string>
|
||||
<string name="reconnect">Reconnect</string>
|
||||
<string name="right_click">Send Right Click</string>
|
||||
<string name="middle_click">Send Middle Click</string>
|
||||
<string name="device_not_paired">Device not paired</string>
|
||||
<string name="request_pairing">Request pairing</string>
|
||||
<string name="pairing_accept">Accept</string>
|
||||
@@ -67,17 +50,7 @@
|
||||
<string name="settings">KDE Connect Settings</string>
|
||||
<string name="mpris_play">Play</string>
|
||||
<string name="mpris_previous">Previous</string>
|
||||
<string name="mpris_rew">Rewind</string>
|
||||
<string name="mpris_ff">Fast-forward</string>
|
||||
<string name="mpris_next">Next</string>
|
||||
<string name="mpris_volume">Volume</string>
|
||||
<string name="share_to">Share To...</string>
|
||||
<string name="protocol_version_older">This device uses an old protocol version</string>
|
||||
<string name="protocol_version_newer">This device uses a newer protocol version</string>
|
||||
<string name="general_settings">General Settings</string>
|
||||
<string name="device_name">Device name</string>
|
||||
<string name="device_name_preference_summary">%s</string>
|
||||
<string name="invalid_device_name">Invalid device name</string>
|
||||
<string name="shareplugin_text_saved">Received text, saved to clipboard</string>
|
||||
|
||||
</resources>
|
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# The name of catalog we create (without the.pot extension), sourced from the scripty scripts
|
||||
FILENAME="kdeconnect-android"
|
||||
|
||||
function export_pot_file # First parameter will be the path of the pot file we have to create, includes $FILENAME
|
||||
{
|
||||
potfile=$1
|
||||
mkdir outdir
|
||||
a2po export --android src/main/res/ --gettext outdir
|
||||
mv outdir/template.pot $potfile
|
||||
rm -rf outdir
|
||||
}
|
||||
|
||||
function import_po_files # First parameter will be a path that will contain several .po files with the format LANG.po
|
||||
{
|
||||
podir=$1
|
||||
a2po import --android src/main/res/ --gettext $podir
|
||||
}
|
||||
|
||||
|
46
build.gradle
@@ -1,46 +1,2 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.12.+'
|
||||
}
|
||||
}
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 19
|
||||
buildToolsVersion '20.0.0'
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 19
|
||||
}
|
||||
packagingOptions {
|
||||
exclude "META-INF/DEPENDENCIES"
|
||||
exclude "META-INF/NOTICE"
|
||||
exclude "META-INF/LICENSE"
|
||||
exclude "META-INF/LICENSE.txt"
|
||||
exclude "META-INF/NOTICE.txt"
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
productFlavors {
|
||||
}
|
||||
buildTypes {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
compile 'com.android.support:support-v4:19.1.+'
|
||||
compile 'com.android.support:appcompat-v7:19.1.+'
|
||||
compile 'org.apache.mina:mina-core:2.0.+'
|
||||
compile 'org.bouncycastle:bcprov-jdk16:1.45'
|
||||
compile 'org.apache.sshd:sshd-core:0.8.0'
|
||||
compile 'tomcat:tomcat-apr:5.5.+'
|
||||
//compile fileTree(dir: 'libs', include: '*.jar')
|
||||
}
|
||||
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Mon Jun 09 09:51:48 CEST 2014
|
||||
#Wed Apr 10 15:27:10 PDT 2013
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
|
||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.6-bin.zip
|
||||
|
1067
icon.svg
Before Width: | Height: | Size: 35 KiB |
@@ -1,73 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="debug" />
|
||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugJava" />
|
||||
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugTest" />
|
||||
<option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
|
||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
||||
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
|
||||
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
<option name="GRADLE_PROJECT_PATH" value=":" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
|
||||
<module external.linked.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/test/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/test/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/test/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/test/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/test/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 19 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="sshd-core-0.8.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="tomcat-apr-5.5.15" level="project" />
|
||||
<orderEntry type="library" exported="" name="slf4j-api-1.6.6" level="project" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-19.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-19.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="bcprov-jdk16-1.45" level="project" />
|
||||
<orderEntry type="library" exported="" name="mina-core-2.0.7" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
1
settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
include ':KdeConnect'
|
@@ -1,201 +0,0 @@
|
||||
package org.kde.kdeconnect.Backends.LanBackend;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.mina.core.future.WriteFuture;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.json.JSONObject;
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.security.PublicKey;
|
||||
|
||||
public class LanLink extends BaseLink {
|
||||
|
||||
private IoSession session = null;
|
||||
|
||||
public void disconnect() {
|
||||
if (session == null) return;
|
||||
//Log.i("LanLink", "Disconnect: "+session.getRemoteAddress().toString());
|
||||
session.close(true);
|
||||
}
|
||||
|
||||
public LanLink(IoSession session, String deviceId, BaseLinkProvider linkProvider) {
|
||||
super(deviceId, linkProvider);
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
private Thread sendPayload(NetworkPackage np) {
|
||||
|
||||
try {
|
||||
|
||||
final InputStream stream = np.getPayload();
|
||||
|
||||
ServerSocket candidateServer = null;
|
||||
boolean success = false;
|
||||
int tcpPort = 1739;
|
||||
while(!success) {
|
||||
try {
|
||||
candidateServer = new ServerSocket();
|
||||
candidateServer.bind(new InetSocketAddress(tcpPort));
|
||||
success = true;
|
||||
} catch(Exception e) {
|
||||
Log.e("LanLink", "Exception openning serversocket: "+e);
|
||||
tcpPort++;
|
||||
if (tcpPort >= 1764) {
|
||||
Log.e("LanLink", "No more ports available");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
JSONObject payloadTransferInfo = new JSONObject();
|
||||
payloadTransferInfo.put("port", tcpPort);
|
||||
|
||||
final ServerSocket server = candidateServer;
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//TODO: Timeout when waiting for a connection and close the socket
|
||||
OutputStream socket = null;
|
||||
try {
|
||||
socket = server.accept().getOutputStream();
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
Log.e("LanLink","Beginning to send payload");
|
||||
while ((bytesRead = stream.read(buffer)) != -1) {
|
||||
//Log.e("ok",""+bytesRead);
|
||||
socket.write(buffer, 0, bytesRead);
|
||||
}
|
||||
Log.e("LanLink","Finished sending payload");
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("LanLink", "Exception with payload upload socket");
|
||||
} finally {
|
||||
if (socket != null) {
|
||||
try { socket.close(); } catch(Exception e) { }
|
||||
}
|
||||
try { server.close(); } catch(Exception e) { }
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
|
||||
np.setPayloadTransferInfo(payloadTransferInfo);
|
||||
|
||||
return thread;
|
||||
|
||||
} catch(Exception e) {
|
||||
|
||||
e.printStackTrace();
|
||||
Log.e("LanLink", "Exception with payload upload socket");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Blocking, do not call from main thread
|
||||
@Override
|
||||
public boolean sendPackage(final NetworkPackage np) {
|
||||
|
||||
if (session == null) {
|
||||
Log.e("LanLink", "sendPackage failed: not yet connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Thread thread = null;
|
||||
if (np.hasPayload()) {
|
||||
thread = sendPayload(np);
|
||||
if (thread == null) return false;
|
||||
}
|
||||
|
||||
WriteFuture future = session.write(np.serialize());
|
||||
future.awaitUninterruptibly();
|
||||
if (!future.isWritten()) return false;
|
||||
|
||||
if (thread != null) {
|
||||
thread.join(); //Wait for thread to finish
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("LanLink", "sendPackage exception");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Blocking, do not call from main thread
|
||||
@Override
|
||||
public boolean sendPackageEncrypted(NetworkPackage np, PublicKey key) {
|
||||
|
||||
if (session == null) {
|
||||
Log.e("LanLink", "sendPackage failed: not yet connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
Thread thread = null;
|
||||
if (np.hasPayload()) {
|
||||
thread = sendPayload(np);
|
||||
if (thread == null) return false;
|
||||
}
|
||||
|
||||
np = np.encrypt(key);
|
||||
WriteFuture future = session.write(np.serialize());
|
||||
if (!future.await().isWritten()) return false;
|
||||
|
||||
if (thread != null) {
|
||||
thread.join(); //Wait for thread to finish
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("LanLink", "sendPackageEncrypted exception");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void injectNetworkPackage(NetworkPackage np) {
|
||||
|
||||
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_ENCRYPTED)) {
|
||||
|
||||
try {
|
||||
np = np.decrypt(privateKey);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("onPackageReceived","Exception reading the key needed to decrypt the package");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (np.hasPayloadTransferInfo()) {
|
||||
|
||||
try {
|
||||
Socket socket = new Socket();
|
||||
int tcpPort = np.getPayloadTransferInfo().getInt("port");
|
||||
InetSocketAddress address = (InetSocketAddress)session.getRemoteAddress();
|
||||
socket.connect(new InetSocketAddress(address.getAddress(), tcpPort));
|
||||
np.setPayload(socket.getInputStream(), np.getPayloadSize());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("LanLink", "Exception connecting to payload remote socket");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
packageReceived(np);
|
||||
}
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
package org.kde.kdeconnect.Backends.LoopbackBackend;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
public class LoopbackLink extends BaseLink {
|
||||
|
||||
public LoopbackLink(BaseLinkProvider linkProvider) {
|
||||
super("loopback", linkProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendPackage(NetworkPackage in) {
|
||||
String s = in.serialize();
|
||||
NetworkPackage out= NetworkPackage.unserialize(s);
|
||||
if (in.hasPayload()) out.setPayload(in.getPayload(), in.getPayloadSize());
|
||||
packageReceived(out);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendPackageEncrypted(NetworkPackage in, PublicKey key) {
|
||||
try {
|
||||
in = in.encrypt(key);
|
||||
String s = in.serialize();
|
||||
NetworkPackage out= NetworkPackage.unserialize(s);
|
||||
out.decrypt(privateKey);
|
||||
packageReceived(out);
|
||||
if (in.hasPayload()) out.setPayload(in.getPayload(), in.getPayloadSize());
|
||||
return true;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("LoopbackLink", "Encryption exception");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
package org.kde.kdeconnect.Backends.LoopbackBackend;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
|
||||
public class LoopbackLinkProvider extends BaseLinkProvider {
|
||||
|
||||
private final Context context;
|
||||
|
||||
public LoopbackLinkProvider(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
onNetworkChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkChange() {
|
||||
|
||||
NetworkPackage np = NetworkPackage.createIdentityPackage(context);
|
||||
connectionAccepted(np, new LoopbackLink(this));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "LoopbackLinkProvider";
|
||||
}
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
public class FilesHelper {
|
||||
|
||||
public static String getFileExt(String fileName) {
|
||||
//return MimeTypeMap.getFileExtensionFromUrl(fileName);
|
||||
return fileName.substring((fileName.lastIndexOf(".") + 1), fileName.length());
|
||||
}
|
||||
|
||||
public static String getMimeTypeFromFile(String file) {
|
||||
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(getFileExt(file));
|
||||
if (mime == null) mime = "*/*";
|
||||
return mime;
|
||||
}
|
||||
|
||||
//Following code from http://activemq.apache.org/maven/5.7.0/kahadb/apidocs/src-html/org/apache/kahadb/util/IOHelper.html
|
||||
/**
|
||||
* Converts any string into a string that is safe to use as a file name.
|
||||
* The result will only include ascii characters and numbers, and the "-","_", and "." characters.
|
||||
*
|
||||
* @param name
|
||||
* @param dirSeparators
|
||||
* @param maxFileLength
|
||||
* @return
|
||||
*/
|
||||
public static String toFileSystemSafeName(String name, boolean dirSeparators, int maxFileLength) {
|
||||
int size = name.length();
|
||||
StringBuilder rc = new StringBuilder(size * 2);
|
||||
for (int i = 0; i < size; i++) {
|
||||
char c = name.charAt(i);
|
||||
boolean valid = c >= 'a' && c <= 'z';
|
||||
valid = valid || (c >= 'A' && c <= 'Z');
|
||||
valid = valid || (c >= '0' && c <= '9');
|
||||
valid = valid || (c == '_') || (c == '-') || (c == '.');
|
||||
valid = valid || (dirSeparators && ( (c == '/') || (c == '\\')));
|
||||
|
||||
if (valid) {
|
||||
rc.append(c);
|
||||
}
|
||||
}
|
||||
String result = rc.toString();
|
||||
if (result.length() > maxFileLength) {
|
||||
result = result.substring(result.length()-maxFileLength,result.length());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public static String toFileSystemSafeName(String name, boolean dirSeparators) {
|
||||
return toFileSystemSafeName(name, dirSeparators, 255);
|
||||
}
|
||||
public static String toFileSystemSafeName(String name) {
|
||||
return toFileSystemSafeName(name, true, 255);
|
||||
}
|
||||
|
||||
}
|
@@ -1,231 +0,0 @@
|
||||
package org.kde.kdeconnect.Plugins.MousePadPlugin;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
public class MousePadActivity extends Activity implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, MousePadGestureDetector.OnGestureListener {
|
||||
private final static float MinDistanceToSendScroll = 2.5f;
|
||||
|
||||
private float mPrevX;
|
||||
private float mPrevY;
|
||||
private float mCurrentX;
|
||||
private float mCurrentY;
|
||||
|
||||
boolean isScrolling = false;
|
||||
|
||||
float accumulatedDistanceY = 0;
|
||||
|
||||
private String deviceId;
|
||||
|
||||
private GestureDetector mDetector;
|
||||
|
||||
private MousePadGestureDetector mMousePadGestureDetector;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_mousepad);
|
||||
deviceId = getIntent().getStringExtra("deviceId");
|
||||
mDetector = new GestureDetector(this, this);
|
||||
mMousePadGestureDetector = new MousePadGestureDetector(this, this);
|
||||
mDetector.setOnDoubleTapListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.menu_mousepad, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_right_click:
|
||||
sendRightClick();
|
||||
return true;
|
||||
case R.id.menu_middle_click:
|
||||
sendMiddleClick();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (mMousePadGestureDetector.onTouchEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( mDetector.onTouchEvent(event) ) {
|
||||
return true;
|
||||
}
|
||||
int actionType = event.getAction();
|
||||
final float x = event.getX();
|
||||
final float y = event.getY();
|
||||
if (isScrolling) {
|
||||
if (actionType == MotionEvent.ACTION_UP) {
|
||||
isScrolling = false;
|
||||
} else {
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
switch (actionType) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mPrevX = x;
|
||||
mPrevY = y;
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mCurrentX = x;
|
||||
mCurrentY = y;
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(deviceId);
|
||||
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
|
||||
if (mousePadPlugin == null) return;
|
||||
mousePadPlugin.sendPoints(mCurrentX - mPrevX, mCurrentY - mPrevY);
|
||||
mPrevX = mCurrentX;
|
||||
mPrevY = mCurrentY;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowPress(MotionEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, final float distanceX, final float distanceY) {
|
||||
// If only one thumb is used then cancel the scroll gesture
|
||||
if (e2.getPointerCount() <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
isScrolling = true;
|
||||
|
||||
accumulatedDistanceY += distanceY;
|
||||
if (accumulatedDistanceY > MinDistanceToSendScroll || accumulatedDistanceY < -MinDistanceToSendScroll)
|
||||
{
|
||||
final float scrollToSendY = accumulatedDistanceY;
|
||||
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(deviceId);
|
||||
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
|
||||
if (mousePadPlugin == null) return;
|
||||
mousePadPlugin.sendScroll(0, scrollToSendY);
|
||||
}
|
||||
});
|
||||
|
||||
accumulatedDistanceY = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(deviceId);
|
||||
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
|
||||
if (mousePadPlugin == null) return;
|
||||
mousePadPlugin.sendSingleClick();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(deviceId);
|
||||
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
|
||||
if (mousePadPlugin == null) return;
|
||||
mousePadPlugin.sendDoubleClick();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTapEvent(MotionEvent e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTripleFingerTap(MotionEvent ev) {
|
||||
sendMiddleClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleFingerTap(MotionEvent ev) {
|
||||
sendRightClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendMiddleClick() {
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(deviceId);
|
||||
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
|
||||
if (mousePadPlugin == null) return;
|
||||
mousePadPlugin.sendMiddleClick();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendRightClick() {
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(deviceId);
|
||||
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
|
||||
if (mousePadPlugin == null) return;
|
||||
mousePadPlugin.sendRightClick();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
package org.kde.kdeconnect.Plugins.MousePadPlugin;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
|
||||
public class MousePadGestureDetector {
|
||||
|
||||
private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout() + 100;
|
||||
private OnGestureListener mGestureListener;
|
||||
|
||||
private Context mCtx;
|
||||
|
||||
private long mFirstDownTime = 0;
|
||||
|
||||
private boolean mIsGestureHandled;
|
||||
|
||||
public interface OnGestureListener {
|
||||
|
||||
boolean onTripleFingerTap(MotionEvent ev);
|
||||
|
||||
boolean onDoubleFingerTap(MotionEvent ev);
|
||||
}
|
||||
|
||||
public MousePadGestureDetector(Context ctx, OnGestureListener gestureListener) {
|
||||
if (gestureListener == null) {
|
||||
throw new IllegalArgumentException("gestureListener cannot be null");
|
||||
}
|
||||
mGestureListener = gestureListener;
|
||||
mCtx = ctx;
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
int action = event.getAction();
|
||||
switch (action & MotionEvent.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mIsGestureHandled = false;
|
||||
mFirstDownTime = event.getEventTime();
|
||||
break;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
int count = event.getPointerCount();
|
||||
if (event.getEventTime() - mFirstDownTime <= TAP_TIMEOUT) {
|
||||
if (count == 3) {
|
||||
if (!mIsGestureHandled) {
|
||||
mIsGestureHandled = mGestureListener.onTripleFingerTap(event);
|
||||
}
|
||||
} else if (count == 2) {
|
||||
if (!mIsGestureHandled) {
|
||||
mIsGestureHandled = mGestureListener.onDoubleFingerTap(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
mFirstDownTime = 0;
|
||||
break;
|
||||
}
|
||||
return mIsGestureHandled;
|
||||
}
|
||||
}
|
@@ -1,112 +0,0 @@
|
||||
package org.kde.kdeconnect.Plugins.MousePadPlugin;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
public class MousePadPlugin extends Plugin {
|
||||
@Override
|
||||
public String getPluginName() {
|
||||
return "plugin_mousepad";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return context.getString(R.string.pref_plugin_mousepad);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return context.getString(R.string.pref_plugin_mousepad_desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return context.getResources().getDrawable(R.drawable.icon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPackageReceived(NetworkPackage np) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog getErrorDialog(Context baseContext) { return null; }
|
||||
|
||||
public void sendPoints(float dx, float dy) {
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
|
||||
np.set("dx", dx);
|
||||
np.set("dy", dy);
|
||||
device.sendPackage(np);
|
||||
}
|
||||
|
||||
public void sendSingleClick() {
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
|
||||
np.set("singleclick", true);
|
||||
device.sendPackage(np);
|
||||
}
|
||||
|
||||
public void sendDoubleClick() {
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
|
||||
np.set("doubleclick", true);
|
||||
device.sendPackage(np);
|
||||
}
|
||||
|
||||
public void sendMiddleClick() {
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
|
||||
np.set("middleclick", true);
|
||||
device.sendPackage(np);
|
||||
}
|
||||
|
||||
public void sendRightClick() {
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
|
||||
np.set("rightclick", true);
|
||||
device.sendPackage(np);
|
||||
}
|
||||
|
||||
public void sendScroll(float dx, float dy) {
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
|
||||
np.set("scroll", true);
|
||||
np.set("dx", dx);
|
||||
np.set("dy", dy);
|
||||
device.sendPackage(np);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Button getInterfaceButton(final Activity activity) {
|
||||
Button button = new Button(activity);
|
||||
button.setText(R.string.open_mousepad);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(activity, MousePadActivity.class);
|
||||
intent.putExtra("deviceId", device.getDeviceId());
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
});
|
||||
return button;
|
||||
}
|
||||
}
|
@@ -1,210 +0,0 @@
|
||||
package org.kde.kdeconnect.Plugins.SftpPlugin;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
import org.apache.sshd.SshServer;
|
||||
import org.apache.sshd.common.NamedFactory;
|
||||
import org.apache.sshd.common.Session;
|
||||
import org.apache.sshd.server.Command;
|
||||
import org.apache.sshd.server.FileSystemFactory;
|
||||
import org.apache.sshd.server.FileSystemView;
|
||||
import org.apache.sshd.server.PasswordAuthenticator;
|
||||
import org.apache.sshd.server.PublickeyAuthenticator;
|
||||
import org.apache.sshd.server.SshFile;
|
||||
import org.apache.sshd.server.command.ScpCommandFactory;
|
||||
import org.apache.sshd.server.filesystem.NativeFileSystemView;
|
||||
import org.apache.sshd.server.filesystem.NativeSshFile;
|
||||
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
|
||||
import org.apache.sshd.server.session.ServerSession;
|
||||
import org.apache.sshd.server.sftp.SftpSubsystem;
|
||||
import org.kde.kdeconnect.Device;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
class SimplePasswordAuthenticator implements PasswordAuthenticator {
|
||||
|
||||
public void setUser(String user) {this.user = user;}
|
||||
public String getUser() {return this.user;}
|
||||
|
||||
public void setPassword(String password) {this.password = password;}
|
||||
public String getPassword() {return this.password;}
|
||||
|
||||
@Override
|
||||
public boolean authenticate(String user, String password, ServerSession session) {
|
||||
return user.equals(this.user) && password.equals(this.password);
|
||||
}
|
||||
|
||||
private String user;
|
||||
private String password;
|
||||
}
|
||||
|
||||
class SimplePublicKeyAuthenticator implements PublickeyAuthenticator {
|
||||
|
||||
private List<PublicKey> keys = new ArrayList<PublicKey>();
|
||||
|
||||
public void addKey(PublicKey key) {
|
||||
keys.add(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean authenticate(String user, PublicKey key, ServerSession session) {
|
||||
for (PublicKey k : keys) {
|
||||
if (key.equals(k)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SimpleSftpServer {
|
||||
private static final int STARTPORT = 1739;
|
||||
private static final int ENDPORT = 1764;
|
||||
|
||||
private static final String USER = "kdeconnect";
|
||||
|
||||
public static int port = -1;
|
||||
private static boolean started = false;
|
||||
|
||||
public final SimplePasswordAuthenticator passwordAuth = new SimplePasswordAuthenticator();
|
||||
public final SimplePublicKeyAuthenticator keyAuth = new SimplePublicKeyAuthenticator();
|
||||
private final SshServer sshd = SshServer.setUpDefaultServer();
|
||||
|
||||
|
||||
public void init(Context ctx, Device device) {
|
||||
passwordAuth.setUser(USER);
|
||||
keyAuth.addKey(device.publicKey);
|
||||
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(ctx.getFilesDir() + "/sftpd.ser"));
|
||||
|
||||
//sshd.setFileSystemFactory(new NativeFileSystemFactory());
|
||||
sshd.setFileSystemFactory(new SecureFileSystemFactory());
|
||||
//sshd.setShellFactory(new ProcessShellFactory(new String[] { "/bin/sh", "-i", "-l" }));
|
||||
sshd.setCommandFactory(new ScpCommandFactory());
|
||||
sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
|
||||
|
||||
sshd.setPasswordAuthenticator(passwordAuth);
|
||||
sshd.setPublickeyAuthenticator(keyAuth);
|
||||
}
|
||||
|
||||
public boolean start() {
|
||||
if (!started) {
|
||||
String password = Long.toHexString(Double.doubleToLongBits(Math.random()));
|
||||
passwordAuth.setPassword(password);
|
||||
|
||||
port = STARTPORT;
|
||||
while(!started) {
|
||||
try {
|
||||
sshd.setPort(port);
|
||||
sshd.start();
|
||||
started = true;
|
||||
} catch(Exception e) {
|
||||
port++;
|
||||
if (port >= ENDPORT) {
|
||||
port = -1;
|
||||
Log.e("SftpServer", "No more ports available");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
try {
|
||||
started = false;
|
||||
sshd.stop();
|
||||
} catch (InterruptedException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public String getLocalIpAddress() {
|
||||
String ip6 = null;
|
||||
try {
|
||||
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
|
||||
NetworkInterface intf = en.nextElement();
|
||||
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
|
||||
InetAddress inetAddress = enumIpAddr.nextElement();
|
||||
if (!inetAddress.isLoopbackAddress()) {
|
||||
String address = inetAddress.getHostAddress();
|
||||
if (InetAddressUtils.isIPv4Address(address)) { //Prefer IPv4 over IPv6, because sshfs doesn't seem to like IPv6
|
||||
return address;
|
||||
} else {
|
||||
ip6 = address;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketException ex) {
|
||||
}
|
||||
return ip6;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SecureFileSystemFactory implements FileSystemFactory {
|
||||
|
||||
public SecureFileSystemFactory() {}
|
||||
|
||||
@Override
|
||||
public FileSystemView createFileSystemView(final Session username) {
|
||||
final String base = "/";
|
||||
return new SecureFileSystemView(base, username.getUsername());
|
||||
}
|
||||
}
|
||||
|
||||
class SecureFileSystemView extends NativeFileSystemView {
|
||||
// the first and the last character will always be '/'
|
||||
// It is always with respect to the root directory.
|
||||
private String currDir = "/";
|
||||
private String rootDir = "/";
|
||||
private String userName;
|
||||
private boolean caseInsensitive = false;
|
||||
//
|
||||
public SecureFileSystemView(final String rootDir, final String userName) {
|
||||
super(userName);
|
||||
this.rootDir = NativeSshFile.normalizeSeparateChar(rootDir);
|
||||
this.userName = userName;
|
||||
}
|
||||
//
|
||||
@Override
|
||||
public SshFile getFile(final String file) {
|
||||
return getFile(currDir, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshFile getFile(final SshFile baseDir, final String file) {
|
||||
return getFile(baseDir.getAbsolutePath(), file);
|
||||
}
|
||||
|
||||
//
|
||||
protected SshFile getFile(final String dir, final String file) {
|
||||
// get actual file object
|
||||
String physicalName = NativeSshFile.getPhysicalName("/", dir, file, caseInsensitive);
|
||||
File fileObj = new File(rootDir, physicalName); // chroot
|
||||
|
||||
// strip the root directory and return
|
||||
String userFileName = physicalName.substring("/".length() - 1);
|
||||
return new SecureSshFile(this, userFileName, fileObj, userName);
|
||||
}
|
||||
}
|
||||
|
||||
class SecureSshFile extends NativeSshFile {
|
||||
//
|
||||
public SecureSshFile(final SecureFileSystemView view, final String fileName, final File file, final String userName) {
|
||||
super(fileName, file, userName);
|
||||
}
|
||||
}
|
@@ -1,79 +0,0 @@
|
||||
package org.kde.kdeconnect.Plugins.SftpPlugin;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Environment;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
public class SftpPlugin extends Plugin {
|
||||
|
||||
private static final SimpleSftpServer server = new SimpleSftpServer();
|
||||
|
||||
/*static {
|
||||
PluginFactory.registerPlugin(SftpPlugin.class);
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public String getPluginName() {return "plugin_sftp";}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return context.getResources().getString(R.string.pref_plugin_sftp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return context.getResources().getString(R.string.pref_plugin_sftp_desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return context.getResources().getDrawable(R.drawable.icon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {return true;}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
server.init(context, device);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPackageReceived(NetworkPackage np) {
|
||||
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_SFTP)) return false;
|
||||
|
||||
if (np.getBoolean("startBrowsing")) {
|
||||
if (server.start()) {
|
||||
NetworkPackage np2 = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_SFTP);
|
||||
np2.set("ip", server.getLocalIpAddress());
|
||||
np2.set("port", server.port);
|
||||
np2.set("user", server.passwordAuth.getUser());
|
||||
np2.set("password", server.passwordAuth.getPassword());
|
||||
np2.set("path", Environment.getExternalStorageDirectory().getAbsolutePath());
|
||||
device.sendPackage(np2);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog getErrorDialog(Context baseContext) {return null;}
|
||||
|
||||
@Override
|
||||
public Button getInterfaceButton(Activity activity) {return null;}
|
||||
|
||||
}
|
@@ -1,243 +0,0 @@
|
||||
package org.kde.kdeconnect.Plugins.SharePlugin;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.util.Log;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.kde.kdeconnect.Helpers.FilesHelper;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
|
||||
public class SharePlugin extends Plugin {
|
||||
|
||||
/*static {
|
||||
PluginFactory.registerPlugin(PingPlugin.class);
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public String getPluginName() {
|
||||
return "share_ping";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return context.getResources().getString(R.string.pref_plugin_sharereceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return context.getResources().getString(R.string.pref_plugin_sharereceiver_desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return context.getResources().getDrawable(R.drawable.icon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPackageReceived(NetworkPackage np) {
|
||||
|
||||
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_SHARE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (np.hasPayload()) {
|
||||
|
||||
Log.e("SharePlugin", "hasPayload");
|
||||
|
||||
final InputStream input = np.getPayload();
|
||||
final int fileLength = np.getPayloadSize();
|
||||
final String filename = np.getString("filename", new Long(System.currentTimeMillis()).toString());
|
||||
|
||||
String deviceDir = FilesHelper.toFileSystemSafeName(device.getName());
|
||||
//Get the external storage and append "/kdeconnect/DEVICE_NAME/"
|
||||
String destinationDir = Environment.getExternalStorageDirectory().getPath();
|
||||
destinationDir = new File(destinationDir, "kdeconnect").getPath();
|
||||
destinationDir = new File(destinationDir, deviceDir).getPath();
|
||||
|
||||
//Create directories if needed
|
||||
new File(destinationDir).mkdirs();
|
||||
|
||||
//Append filename to the destination path
|
||||
final File destinationFullPath = new File(destinationDir, filename);
|
||||
|
||||
Log.e("SharePlugin", "destinationFullPath:" + destinationFullPath);
|
||||
|
||||
final int notificationId = (int)System.currentTimeMillis();
|
||||
Resources res = context.getResources();
|
||||
Notification noti = new NotificationCompat.Builder(context)
|
||||
.setContentTitle(res.getString(R.string.incoming_file_title, device.getName()))
|
||||
.setContentText(res.getString(R.string.incoming_file_text, filename))
|
||||
.setTicker(res.getString(R.string.incoming_file_title, device.getName()))
|
||||
.setSmallIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setAutoCancel(true)
|
||||
.build();
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(notificationId, noti);
|
||||
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
OutputStream output = new FileOutputStream(destinationFullPath.getPath());
|
||||
|
||||
byte data[] = new byte[1024];
|
||||
long total = 0;
|
||||
int count;
|
||||
while ((count = input.read(data)) >= 0) {
|
||||
total += count;
|
||||
output.write(data, 0, count);
|
||||
if (fileLength > 0) {
|
||||
if (total >= fileLength) break;
|
||||
float progress = (total * 100 / fileLength);
|
||||
}
|
||||
//else Log.e("SharePlugin", "Infinite loop? :D");
|
||||
}
|
||||
|
||||
output.flush();
|
||||
output.close();
|
||||
input.close();
|
||||
|
||||
Log.e("SharePlugin", "Transfer finished");
|
||||
|
||||
//Make sure it is added to the Android Gallery
|
||||
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
mediaScanIntent.setData(Uri.fromFile(destinationFullPath));
|
||||
context.sendBroadcast(mediaScanIntent);
|
||||
|
||||
//Update the notification and allow to open the file from it
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(Uri.fromFile(destinationFullPath), FilesHelper.getMimeTypeFromFile(destinationFullPath.getPath()));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
|
||||
stackBuilder.addNextIntent(intent);
|
||||
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
|
||||
0,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
|
||||
Resources res = context.getResources();
|
||||
Notification noti = new NotificationCompat.Builder(context)
|
||||
.setContentTitle(res.getString(R.string.received_file_title, device.getName()))
|
||||
.setContentText(res.getString(R.string.received_file_text, filename))
|
||||
.setContentIntent(resultPendingIntent)
|
||||
.setTicker(res.getString(R.string.received_file_title, device.getName()))
|
||||
.setSmallIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setAutoCancel(true)
|
||||
.setDefaults(Notification.DEFAULT_SOUND)
|
||||
.build();
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(notificationId, noti);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("SharePlugin", "Receiver thread exception");
|
||||
e.printStackTrace();
|
||||
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
} else if (np.has("text")) {
|
||||
Log.e("SharePlugin", "hasText");
|
||||
|
||||
String text = np.getString("text");
|
||||
if(android.os.Build.VERSION.SDK_INT >= 11) {
|
||||
ClipboardManager cm = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cm.setText(text);
|
||||
} else {
|
||||
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
clipboard.setText(text);
|
||||
}
|
||||
Toast.makeText(context, R.string.shareplugin_text_saved, Toast.LENGTH_LONG).show();
|
||||
} else if (np.has("url")) {
|
||||
|
||||
String url = np.getString("url");
|
||||
|
||||
Log.e("SharePlugin", "hasUrl: "+url);
|
||||
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
//Do not launch it directly, show a notification instead
|
||||
//context.startActivity(browserIntent);
|
||||
|
||||
Resources res = context.getResources();
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
|
||||
stackBuilder.addNextIntent(browserIntent);
|
||||
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
|
||||
0,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
|
||||
Notification noti = new NotificationCompat.Builder(context)
|
||||
.setContentTitle(res.getString(R.string.received_url_title, device.getName()))
|
||||
.setContentText(res.getString(R.string.received_url_text, url))
|
||||
.setContentIntent(resultPendingIntent)
|
||||
.setTicker(res.getString(R.string.received_url_title, device.getName()))
|
||||
.setSmallIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setAutoCancel(true)
|
||||
.setDefaults(Notification.DEFAULT_SOUND)
|
||||
.build();
|
||||
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify((int)System.currentTimeMillis(), noti);
|
||||
|
||||
} else {
|
||||
Log.e("SharePlugin", "Error: Nothing attached!");
|
||||
}
|
||||
|
||||
|
||||
} catch(Exception e) {
|
||||
Log.e("SharePlugin","Exception");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog getErrorDialog(Context baseContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Button getInterfaceButton(Activity activity) { return null; }
|
||||
|
||||
}
|
@@ -1,177 +0,0 @@
|
||||
package org.kde.kdeconnect.UserInterface;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.UserInterface.List.ButtonItem;
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||
import org.kde.kdeconnect.UserInterface.List.SectionItem;
|
||||
import org.kde.kdeconnect.UserInterface.List.TextItem;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class DeviceActivity extends ActionBarActivity {
|
||||
|
||||
static private String deviceId; //Static because if we get here by using the back button in the action bar, the extra deviceId will not be set.
|
||||
private Device device;
|
||||
|
||||
private final Device.PluginsChangedListener pluginsChangedListener = new Device.PluginsChangedListener() {
|
||||
@Override
|
||||
public void onPluginsChanged(final Device device) {
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
|
||||
//Errors list
|
||||
final HashMap<String, Plugin> failedPlugins = device.getFailedPlugins();
|
||||
final String[] ids = failedPlugins.keySet().toArray(new String[failedPlugins.size()]);
|
||||
String[] names = new String[failedPlugins.size()];
|
||||
for(int i = 0; i < ids.length; i++) {
|
||||
Plugin p = failedPlugins.get(ids[i]);
|
||||
names[i] = p.getDisplayName();
|
||||
}
|
||||
ListView errorList = (ListView)findViewById(R.id.errors_list);
|
||||
if (!failedPlugins.isEmpty() && errorList.getHeaderViewsCount() == 0) {
|
||||
TextView header = new TextView(DeviceActivity.this);
|
||||
header.setPadding(0,24,0,0);
|
||||
header.setText(getResources().getString(R.string.plugins_failed_to_load));
|
||||
errorList.addHeaderView(header);
|
||||
}
|
||||
errorList.setAdapter(new ArrayAdapter<String>(DeviceActivity.this, android.R.layout.simple_list_item_1, names));
|
||||
errorList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
||||
if (position == 0) return;
|
||||
Plugin p = failedPlugins.get(ids[position - 1]); //Header is position 0, so we have to subtract one
|
||||
p.getErrorDialog(DeviceActivity.this).show();
|
||||
}
|
||||
});
|
||||
|
||||
//Buttons list
|
||||
ArrayList<ListAdapter.Item> items = new ArrayList<ListAdapter.Item>();
|
||||
|
||||
if (device.isReachable()) {
|
||||
final Collection<Plugin> plugins = device.getLoadedPlugins().values();
|
||||
for (Plugin p : plugins) {
|
||||
Button b = p.getInterfaceButton(DeviceActivity.this);
|
||||
if (b != null) {
|
||||
items.add(new SectionItem(p.getDisplayName()));
|
||||
items.add(new ButtonItem(b));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button b = new Button(DeviceActivity.this);
|
||||
b.setText(R.string.device_menu_unpair);
|
||||
b.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
device.unpair();
|
||||
finish();
|
||||
}
|
||||
});
|
||||
items.add(new TextItem(getString(R.string.device_not_reachable)));
|
||||
items.add(new ButtonItem(b));
|
||||
}
|
||||
|
||||
ListView buttonsList = (ListView)findViewById(R.id.buttons_list);
|
||||
buttonsList.setAdapter(new ListAdapter(DeviceActivity.this, items));
|
||||
|
||||
} catch(ConcurrentModificationException e) {
|
||||
Log.e("DeviceActivity", "ConcurrentModificationException");
|
||||
this.run(); //Try again
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_device);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
if (getIntent().hasExtra("deviceId")) {
|
||||
deviceId = getIntent().getStringExtra("deviceId");
|
||||
}
|
||||
|
||||
BackgroundService.RunCommand(DeviceActivity.this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
device = service.getDevice(deviceId);
|
||||
if (device == null) return;
|
||||
setTitle(device.getName());
|
||||
device.addPluginsChangedListener(pluginsChangedListener);
|
||||
pluginsChangedListener.onPluginsChanged(device);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
BackgroundService.RunCommand(DeviceActivity.this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(deviceId);
|
||||
device.removePluginsChangedListener(pluginsChangedListener);
|
||||
}
|
||||
});
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
menu.clear();
|
||||
if (device.isPaired()) {
|
||||
menu.add(R.string.device_menu_plugins).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
Intent intent = new Intent(DeviceActivity.this, SettingsActivity.class);
|
||||
intent.putExtra("deviceId", deviceId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
menu.add(R.string.device_menu_unpair).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
device.unpair();
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
package org.kde.kdeconnect.UserInterface.List;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
public class EntryItem implements ListAdapter.Item {
|
||||
|
||||
private final String title;
|
||||
|
||||
public EntryItem(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View inflateView(LayoutInflater layoutInflater) {
|
||||
View v = layoutInflater.inflate(R.layout.list_item_entry, null);
|
||||
|
||||
TextView titleView = (TextView)v.findViewById(R.id.list_item_entry_title);
|
||||
if (titleView != null) titleView.setText(title);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
package org.kde.kdeconnect.UserInterface.List;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import android.R;
|
||||
|
||||
public class TextItem implements ListAdapter.Item {
|
||||
|
||||
private final String title;
|
||||
|
||||
public TextItem(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View inflateView(LayoutInflater layoutInflater) {
|
||||
|
||||
TextView v = new TextView(layoutInflater.getContext());
|
||||
v.setText(title);
|
||||
v.setTextAppearance(layoutInflater.getContext(), R.style.TextAppearance_DeviceDefault_Medium);
|
||||
return v;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,112 +0,0 @@
|
||||
package org.kde.kdeconnect.UserInterface;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
public class MainSettingsActivity extends PreferenceActivity {
|
||||
|
||||
public static final String KEY_DEVICE_NAME_PREFERENCE = "device_name_preference";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
initializeDeviceName(this);
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
||||
addPreferencesOldApi();
|
||||
} else {
|
||||
getFragmentManager().beginTransaction().
|
||||
replace(android.R.id.content, new GeneralPrefsFragment()).commit();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void addPreferencesOldApi() {
|
||||
addPreferencesFromResource(R.xml.general_preferences);
|
||||
initPreferences((EditTextPreference) findPreference(KEY_DEVICE_NAME_PREFERENCE));
|
||||
}
|
||||
|
||||
private void initPreferences(final EditTextPreference deviceNamePref) {
|
||||
final SharedPreferences sharedPreferences=PreferenceManager.getDefaultSharedPreferences(this);
|
||||
deviceNamePref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newDeviceName) {
|
||||
if (newDeviceName.toString().isEmpty()) {
|
||||
Toast.makeText(
|
||||
MainSettingsActivity.this,
|
||||
getString(R.string.invalid_device_name),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}else{
|
||||
Log.i("MainSettingsActivity", "New device name: " + newDeviceName);
|
||||
deviceNamePref.setSummary(getString(
|
||||
R.string.device_name_preference_summary,
|
||||
newDeviceName.toString()));
|
||||
|
||||
//Broadcast the device information again since it has changed
|
||||
BackgroundService.RunCommand(MainSettingsActivity.this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
service.onNetworkChange();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
deviceNamePref.setSummary(getString(
|
||||
R.string.device_name_preference_summary,
|
||||
sharedPreferences.getString(KEY_DEVICE_NAME_PREFERENCE,"")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Until now it sets only the default deviceName (if not already set).
|
||||
* It's safe to call this multiple time because doesn't override any previous value.
|
||||
* @param context the application context
|
||||
*/
|
||||
public static void initializeDeviceName(Context context){
|
||||
// I could have used getDefaultSharedPreferences(context).contains but we need to check
|
||||
// to checkAgainst empty String also.
|
||||
String deviceName=PreferenceManager.getDefaultSharedPreferences(context).getString(
|
||||
KEY_DEVICE_NAME_PREFERENCE,
|
||||
"");
|
||||
if(deviceName.isEmpty()){
|
||||
Log.i("MainSettingsActivity", "New device name: " + deviceName);
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(
|
||||
KEY_DEVICE_NAME_PREFERENCE,
|
||||
DeviceHelper.getDeviceName()).commit();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static class GeneralPrefsFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.general_preferences);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
if (getActivity() != null) {
|
||||
((MainSettingsActivity)getActivity()).initPreferences(
|
||||
(EditTextPreference) findPreference(KEY_DEVICE_NAME_PREFERENCE));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,302 +0,0 @@
|
||||
package org.kde.kdeconnect.UserInterface;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
import org.kde.kdeconnect.UserInterface.List.EntryItem;
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||
import org.kde.kdeconnect.UserInterface.List.SectionItem;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
public class ShareToReceiver extends ActionBarActivity {
|
||||
|
||||
|
||||
//
|
||||
// Action bar
|
||||
//
|
||||
|
||||
private MenuItem menuProgress;
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.main, menu);
|
||||
menuProgress = menu.findItem(R.id.menu_progress);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_refresh:
|
||||
updateComputerList();
|
||||
BackgroundService.RunCommand(ShareToReceiver.this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
service.onNetworkChange();
|
||||
}
|
||||
});
|
||||
item.setVisible(false);
|
||||
menuProgress.setVisible(true);
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try { Thread.sleep(1500); } catch (InterruptedException e) { }
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
menuProgress.setVisible(false);
|
||||
item.setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateComputerList() {
|
||||
|
||||
final Intent intent = getIntent();
|
||||
|
||||
String action = intent.getAction();
|
||||
if (!Intent.ACTION_SEND.equals(action) && !Intent.ACTION_SEND_MULTIPLE.equals(action)) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(final BackgroundService service) {
|
||||
|
||||
Collection<Device> devices = service.getDevices().values();
|
||||
final ArrayList<Device> devicesList = new ArrayList<Device>();
|
||||
final ArrayList<ListAdapter.Item> items = new ArrayList<ListAdapter.Item>();
|
||||
|
||||
items.add(new SectionItem(getString(R.string.share_to)));
|
||||
|
||||
for (Device d : devices) {
|
||||
if (d.isReachable() && d.isPaired()) {
|
||||
devicesList.add(d);
|
||||
items.add(new EntryItem(d.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ListView list = (ListView) findViewById(R.id.listView1);
|
||||
list.setAdapter(new ListAdapter(ShareToReceiver.this, items));
|
||||
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
|
||||
Device device = devicesList.get(i-1); //NOTE: -1 because of the title!
|
||||
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras.containsKey(Intent.EXTRA_STREAM)) {
|
||||
|
||||
try {
|
||||
|
||||
ArrayList<Uri> uriList;
|
||||
if (!Intent.ACTION_SEND.equals(intent.getAction())) {
|
||||
uriList = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
} else {
|
||||
Uri uri = extras.getParcelable(Intent.EXTRA_STREAM);
|
||||
uriList = new ArrayList<Uri>();
|
||||
uriList.add(uri);
|
||||
}
|
||||
|
||||
queuedSendUriList(device, uriList);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(this.getClass().getName(), e.toString());
|
||||
}
|
||||
|
||||
} else if (extras.containsKey(Intent.EXTRA_TEXT)) {
|
||||
String text = extras.getString(Intent.EXTRA_TEXT);
|
||||
boolean isUrl;
|
||||
try {
|
||||
new URL(text);
|
||||
isUrl = true;
|
||||
} catch(Exception e) {
|
||||
isUrl = false;
|
||||
}
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_SHARE);
|
||||
if (isUrl) {
|
||||
np.set("url", text);
|
||||
} else {
|
||||
np.set("text", text);
|
||||
}
|
||||
device.sendPackage(np);
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void queuedSendUriList(final Device device, final ArrayList<Uri> uriList) {
|
||||
try {
|
||||
Uri uri = uriList.remove(0);
|
||||
ContentResolver cr = getContentResolver();
|
||||
InputStream inputStream = cr.openInputStream(uri);
|
||||
|
||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_SHARE);
|
||||
int size = -1;
|
||||
|
||||
if (uri.getScheme().equals("file")) {
|
||||
// file:// is a non media uri, so we cannot query the ContentProvider
|
||||
|
||||
np.set("filename", uri.getLastPathSegment());
|
||||
|
||||
try {
|
||||
size = (int)new File(uri.getPath()).length();
|
||||
np.setPayload(inputStream, size);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("ShareToReceiver", "Could not obtain file size");
|
||||
}
|
||||
|
||||
}else{
|
||||
// Probably a content:// uri, so we query the Media content provider
|
||||
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
String[] proj = { MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.SIZE, MediaStore.MediaColumns.DISPLAY_NAME };
|
||||
cursor = getContentResolver().query(uri, proj, null, null, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
|
||||
cursor.moveToFirst();
|
||||
String path = cursor.getString(column_index);
|
||||
np.set("filename", Uri.parse(path).getLastPathSegment());
|
||||
np.set("size", (int)new File(path).length());
|
||||
} catch(Exception _) {
|
||||
|
||||
Log.e("ShareToReceiver", "Could not resolve media to a file, trying to get info as media");
|
||||
|
||||
try {
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||
cursor.moveToFirst();
|
||||
String name = cursor.getString(column_index);
|
||||
np.set("filename", name);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("ShareToReceiver", "Could not obtain file name");
|
||||
}
|
||||
|
||||
try {
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE);
|
||||
cursor.moveToFirst();
|
||||
//For some reason this size can differ from the actual file size!
|
||||
size = cursor.getInt(column_index);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("ShareToReceiver", "Could not obtain file size");
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
np.setPayload(inputStream, size);
|
||||
|
||||
}
|
||||
|
||||
device.sendPackage(np, new Device.SendPackageFinishedCallback() {
|
||||
@Override
|
||||
public void sendSuccessful() {
|
||||
if (!uriList.isEmpty()) queuedSendUriList(device, uriList);
|
||||
else Log.e("ShareToReceiver", "All files sent");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFailed() {
|
||||
Log.e("ShareToReceiver", "Failed to send file");
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("ShareToReceiver", "Exception sending files");
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
service.onNetworkChange();
|
||||
service.setDeviceListChangedCallback(new BackgroundService.DeviceListChangedCallback() {
|
||||
@Override
|
||||
public void onDeviceListChanged() {
|
||||
updateComputerList();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
service.setDeviceListChangedCallback(null);
|
||||
}
|
||||
});
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updateComputerList();
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 557 B |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 713 B |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 750 B |
Before Width: | Height: | Size: 365 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 3.0 KiB |
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/mousepad_view">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/mousepad_info"
|
||||
android:gravity="center"
|
||||
style="@android:style/TextAppearance.Medium"
|
||||
android:layout_centerInParent="true"
|
||||
android:padding="12dip" />
|
||||
|
||||
</RelativeLayout>
|