Compare commits
261 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
debbe4fe2c | ||
|
5724a66561 | ||
|
91fe9d63b0 | ||
|
a90928188a | ||
|
256ca60036 | ||
|
05fd6fe5b0 | ||
|
6b9a691f24 | ||
|
d18b61f2a1 | ||
|
6448258648 | ||
|
e5e9490f19 | ||
|
9f7c5be533 | ||
|
f690105181 | ||
|
dafe9eb023 | ||
|
cfbe6775d0 | ||
|
6365143c2c | ||
|
f3fbe4d035 | ||
|
2862e5164e | ||
|
6fae1d350d | ||
|
647f71bf98 | ||
|
343b2ed5e8 | ||
|
eb7351a803 | ||
|
7e7d6c4d82 | ||
|
59d01763c7 | ||
|
5b2756b7f8 | ||
|
5912611aab | ||
|
54d24848e5 | ||
|
638c9df328 | ||
|
9943c36877 | ||
|
03bcfecb88 | ||
|
1b4ade0ae1 | ||
|
b9890210ad | ||
|
1509bfaf6d | ||
|
a22ffd611a | ||
|
adb88560b5 | ||
|
03ede77bd1 | ||
|
6afbe93c2d | ||
|
dd4421dce2 | ||
|
262f811bba | ||
|
0b52cbe36f | ||
|
8542aaf4fa | ||
|
c9d11f650c | ||
|
6c5ed3094b | ||
|
5aaec15e79 | ||
|
b0a632fe10 | ||
|
4d1464484e | ||
|
d6cc84ccfb | ||
|
624cee5d87 | ||
|
a8664d01ec | ||
|
bd68f71af6 | ||
|
8ec828d6e4 | ||
|
a44d48d527 | ||
|
804642c5fc | ||
|
8e0daa69cc | ||
|
400b35216d | ||
|
4a5545529a | ||
|
b2fa8ab506 | ||
|
b9a0b3d2f0 | ||
|
e8c6784ce0 | ||
|
e3b8bb5ffc | ||
|
103bc69347 | ||
|
2b45c82a2e | ||
|
0d3232a5a1 | ||
|
ed5ddc37c8 | ||
|
48bc56f52a | ||
|
5de10b05e7 | ||
|
1fe2590ec8 | ||
|
d02684c714 | ||
|
80ed8a58d5 | ||
|
962501fe38 | ||
|
60c1e67e66 | ||
|
318b82941f | ||
|
acdf8d467e | ||
|
ceee1ee1f6 | ||
|
071f71cb9c | ||
|
4adf2cfbbd | ||
|
de4fa31b2a | ||
|
4deba4ea4f | ||
|
07a8b107eb | ||
|
3b750c78ab | ||
|
47790a710a | ||
|
92451df32f | ||
|
55eada9dfc | ||
|
0ed73f6729 | ||
|
c8e24bf07d | ||
|
f016a06c32 | ||
|
23463afc33 | ||
|
45fd3f88e8 | ||
|
b54e2ea36e | ||
|
3043ca5d05 | ||
|
47368a27fd | ||
|
046a1cb676 | ||
|
43c6742e21 | ||
|
5b2f863e0e | ||
|
a12e64ca59 | ||
|
e99ba38eac | ||
|
0730c52741 | ||
|
093b722f00 | ||
|
162fc8b8dd | ||
|
c648185ffa | ||
|
af915b5756 | ||
|
dc60968a35 | ||
|
c122a0d2f3 | ||
|
ce597f2e51 | ||
|
7ae687ae11 | ||
|
53bb376990 | ||
|
cc10538dcb | ||
|
9c7aaf2ed9 | ||
|
0283b0112e | ||
|
906fc35abb | ||
|
8bc3e69ce6 | ||
|
020d2bca2a | ||
|
50682935e0 | ||
|
1da74a6c68 | ||
|
aa33f4cd08 | ||
|
0c142928f9 | ||
|
24aefebb0e | ||
|
7a47bd0b9c | ||
|
cbb3787a15 | ||
|
61048e5df4 | ||
|
8644f7ee15 | ||
|
77f9a30bd3 | ||
|
1dbc93ec73 | ||
|
64c5362b1c | ||
|
0c4ae62061 | ||
|
727c845624 | ||
|
577d9a5dc8 | ||
|
0311163829 | ||
|
76fd6d1e08 | ||
|
a79e8f83ce | ||
|
3f6ec6a2d7 | ||
|
726f623eea | ||
|
0db8f7c1d4 | ||
|
7a289fbe38 | ||
|
41bb2ed916 | ||
|
4598f080a2 | ||
|
ecf22e889b | ||
|
8598cf5f2e | ||
|
1def9cc9ce | ||
|
0772a37f4e | ||
|
16edb35a0e | ||
|
8f9bad7a0f | ||
|
4fd96062f6 | ||
|
a0a9eca060 | ||
|
5999ef7c8e | ||
|
9542597424 | ||
|
11e5563d9a | ||
|
f31afdb6d0 | ||
|
27ec7c5bfb | ||
|
49360816a3 | ||
|
dd364bd562 | ||
|
809f37a541 | ||
|
36c3df7a07 | ||
|
34aec7120e | ||
|
0a494b002d | ||
|
7298247dfc | ||
|
72a97d9e1c | ||
|
7cc80baf16 | ||
|
0600b1ee2e | ||
|
2f0059938d | ||
|
a023316609 | ||
|
d94ed56c4e | ||
|
15a11cce02 | ||
|
36530b9580 | ||
|
f54acd7308 | ||
|
cfe9ccdc31 | ||
|
52c7581f42 | ||
|
a57dc71ad1 | ||
|
77d06cfe77 | ||
|
13a7c1e5fe | ||
|
5ccf215a54 | ||
|
fddf3c64b5 | ||
|
fb02b9bdf1 | ||
|
8453156d16 | ||
|
9bbe2908ed | ||
|
fb843f8789 | ||
|
c85619dfd2 | ||
|
de9f78adf3 | ||
|
9021ab9454 | ||
|
10210b3972 | ||
|
f83b9cbe0c | ||
|
7d821e7c88 | ||
|
02070343fe | ||
|
416a000dd3 | ||
|
a15cad9ec5 | ||
|
e98100f5f0 | ||
|
d19e7d2052 | ||
|
4e02c674f8 | ||
|
6aa49757fc | ||
|
3c10312d15 | ||
|
26e3363b30 | ||
|
4033233ec1 | ||
|
34e53576e1 | ||
|
a15d76c588 | ||
|
1acea73383 | ||
|
6c8fdb0be7 | ||
|
749170a29c | ||
|
81b08cf89f | ||
|
786ea7658f | ||
|
996516ec88 | ||
|
a36e4a0c88 | ||
|
e08de81c73 | ||
|
30d2665b12 | ||
|
c14946e9e4 | ||
|
1aab14fd50 | ||
|
549fc01c1d | ||
|
d59de8a0b6 | ||
|
8823277e74 | ||
|
30cf27e835 | ||
|
9dbe1ec9cf | ||
|
0595662efc | ||
|
6609ae665c | ||
|
2f26fa5188 | ||
|
6471bafdd9 | ||
|
0620810e4b | ||
|
db9209765d | ||
|
e459d792d0 | ||
|
31fcc3e184 | ||
|
544b5e8c87 | ||
|
7819ae1ca2 | ||
|
98885d6c35 | ||
|
485e646f6f | ||
|
dfece0a414 | ||
|
bbaf40165c | ||
|
b221fa04bf | ||
|
07bf1e9ebc | ||
|
a07c9a9fb7 | ||
|
40a9171985 | ||
|
69197fd657 | ||
|
55e3861c5c | ||
|
5d00e6e168 | ||
|
967635a2f4 | ||
|
58b31fa032 | ||
|
f0d06cfcc4 | ||
|
0fe3b6cd1e | ||
|
f8f58ef2a2 | ||
|
d66f8442fa | ||
|
4b3af91c71 | ||
|
87174cfb28 | ||
|
3517035605 | ||
|
37c2a0e112 | ||
|
754123b9c5 | ||
|
fa11204365 | ||
|
7cd5b905be | ||
|
cda2f573bf | ||
|
50cb2b9f7f | ||
|
513a2b32f4 | ||
|
1757b2e650 | ||
|
091836646a | ||
|
0d8f84037b | ||
|
aabd06f5cb | ||
|
6dbe7bac91 | ||
|
593cf8cac0 | ||
|
46ad74f539 | ||
|
66766e8ab0 | ||
|
34fe3ac272 | ||
|
8800c4503c | ||
|
5200ceaab5 | ||
|
6a9419401e | ||
|
b1ca8757c6 | ||
|
e735e0ecf3 | ||
|
63f0458096 |
9
.gitignore
vendored
@@ -1,4 +1,7 @@
|
|||||||
local.properties
|
local.properties
|
||||||
.gradle
|
.gradle/
|
||||||
.idea
|
.idea/
|
||||||
out
|
out/
|
||||||
|
build/
|
||||||
|
target/
|
||||||
|
classes/
|
3
.reviewboardrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
REVIEWBOARD_URL = "https://git.reviewboard.kde.org"
|
||||||
|
REPOSITORY = 'git://anongit.kde.org/kdeconnect-android'
|
||||||
|
TARGET_GROUPS = 'kdeconnect'
|
339
COPYING
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
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
@@ -1 +0,0 @@
|
|||||||
build/
|
|
@@ -1,79 +0,0 @@
|
|||||||
<?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>
|
|
||||||
|
|
@@ -1,26 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,37 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,65 +0,0 @@
|
|||||||
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";
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,148 +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_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,28 +0,0 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class PreferenceListAdapter extends ArrayAdapter<Preference> {
|
|
||||||
|
|
||||||
|
|
||||||
private ArrayList<Preference> localList;
|
|
||||||
|
|
||||||
public PreferenceListAdapter(Context context, ArrayList<Preference> items) {
|
|
||||||
super(context,0, items);
|
|
||||||
localList = items;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
Preference preference = localList.get(position);
|
|
||||||
return preference.getView(convertView, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,70 +0,0 @@
|
|||||||
package org.kde.kdeconnect.UserInterface;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import org.kde.kdeconnect.BackgroundService;
|
|
||||||
import org.kde.kdeconnect.Device;
|
|
||||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class SettingsActivity extends ListActivity {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
final String deviceId = getIntent().getStringExtra("deviceId");
|
|
||||||
BackgroundService.RunCommand(getApplicationContext(), new BackgroundService.InstanceCallback() {
|
|
||||||
@Override
|
|
||||||
public void onServiceStart(BackgroundService service) {
|
|
||||||
|
|
||||||
final Device device = service.getDevice(deviceId);
|
|
||||||
Set<String> plugins = PluginFactory.getAvailablePlugins();
|
|
||||||
|
|
||||||
final ArrayList<Preference> preferences = new ArrayList<Preference>();
|
|
||||||
for (final String pluginName : plugins) {
|
|
||||||
CheckBoxPreference pref = new CheckBoxPreference(getBaseContext());
|
|
||||||
PluginFactory.PluginInfo info = PluginFactory.getPluginInfo(getBaseContext(), pluginName);
|
|
||||||
pref.setKey(pluginName);
|
|
||||||
pref.setTitle(info.getDisplayName());
|
|
||||||
pref.setSummary(info.getDescription());
|
|
||||||
pref.setChecked(device.isPluginEnabled(pluginName));
|
|
||||||
preferences.add(pref);
|
|
||||||
}
|
|
||||||
|
|
||||||
setListAdapter(new PreferenceListAdapter(SettingsActivity.this, preferences));
|
|
||||||
getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
|
||||||
|
|
||||||
CheckBoxPreference pref = (CheckBoxPreference)preferences.get(i);
|
|
||||||
|
|
||||||
boolean enabled = device.isPluginEnabled(pref.getKey());
|
|
||||||
device.setPluginEnabled(pref.getKey(), !enabled);
|
|
||||||
|
|
||||||
pref.setChecked(!enabled);
|
|
||||||
|
|
||||||
getListAdapter().getView(i, view, null); //This will refresh the view (yes, this is the way to do it)
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getListView().setPadding(16,16,16,16);
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,26 +0,0 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
|
||||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
|
||||||
android:paddingTop="@dimen/activity_vertical_margin"
|
|
||||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
|
||||||
tools:context=".MainActivity"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<ListView
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
android:id="@+id/buttons_list"
|
|
||||||
android:layout_weight="1"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ListView
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
android:id="@+id/errors_list"
|
|
||||||
android:layout_weight="1"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
@@ -1,48 +0,0 @@
|
|||||||
<?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>
|
|
@@ -1,21 +0,0 @@
|
|||||||
<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>
|
|
@@ -1,56 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<string name="pref_plugin_telephony">Telephony notifier</string>
|
|
||||||
<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_clipboard">Clipboard sync</string>
|
|
||||||
<string name="pref_plugin_clipboard_desc">Share the clipboard content</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="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>
|
|
||||||
<string name="cancel">Cancel</string>
|
|
||||||
<string name="open_settings">Open settings</string>
|
|
||||||
<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="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="unknown_device">Unknown device</string>
|
|
||||||
<string name="error_not_reachable">Device not reachable</string>
|
|
||||||
<string name="error_already_requested">Pairing already requested</string>
|
|
||||||
<string name="error_already_paired">Device already paired</string>
|
|
||||||
<string name="error_could_not_send_package">Could not send package</string>
|
|
||||||
<string name="error_timed_out">Timed out</string>
|
|
||||||
<string name="error_canceled_by_user">Canceled by user</string>
|
|
||||||
<string name="error_canceled_by_other_peer">Canceled by other peer</string>
|
|
||||||
<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="tap_to_answer">Tap to answer</string>
|
|
||||||
<string name="reconnect">Reconnect</string>
|
|
||||||
<string name="device_not_paired">Device not paired</string>
|
|
||||||
<string name="request_pairing">Request pairing</string>
|
|
||||||
<string name="pairing_accept">Accept</string>
|
|
||||||
<string name="pairing_reject">Reject</string>
|
|
||||||
<string name="device">Device</string>
|
|
||||||
<string name="pair_device">Pair device</string>
|
|
||||||
<string name="remote_control">Remote control</string>
|
|
||||||
<string name="settings">KDE Connect Settings</string>
|
|
||||||
<string name="mpris_play">Play</string>
|
|
||||||
<string name="mpris_previous">Previous</string>
|
|
||||||
<string name="mpris_next">Next</string>
|
|
||||||
<string name="mpris_volume">Volume</string>
|
|
||||||
|
|
||||||
</resources>
|
|
21
StaticMessages.sh
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
44
build.gradle
@@ -1,2 +1,44 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:0.12.+'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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 @@
|
|||||||
#Wed Apr 10 15:27:10 PDT 2013
|
#Mon Jun 09 09:51:48 CEST 2014
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.6-bin.zip
|
distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
|
||||||
|
1067
icon.svg
Normal file
After Width: | Height: | Size: 35 KiB |
@@ -1,13 +1,92 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<module external.linked.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
<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="NewModuleRootManager" inherit-compiler-output="true">
|
<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="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugTestSources" />
|
||||||
|
<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" />
|
||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<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/assets" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" 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/assets" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" 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/assets" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" 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/assets" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="jdk" jdkName="Android API 19 Platform" jdkType="Android SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" exported="" name="support-v4-19.1.0" level="project" />
|
||||||
|
<orderEntry type="library" exported="" name="sshd-core-0.8.0" level="project" />
|
||||||
|
<orderEntry type="library" exported="" name="bcprov-jdk16-1.45" level="project" />
|
||||||
|
<orderEntry type="library" exported="" name="appcompat-v7-19.1.0" level="project" />
|
||||||
|
<orderEntry type="library" exported="" name="mina-core-2.0.8" 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" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
include ':KdeConnect'
|
|
@@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.kde.kdeconnect_tp"
|
package="org.kde.kdeconnect_tp"
|
||||||
android:versionCode="4"
|
android:versionCode="732"
|
||||||
android:versionName="0.2.1">
|
android:versionName="0.7.3.2">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="9"
|
<uses-sdk android:minSdkVersion="9"
|
||||||
android:targetSdkVersion="18" />
|
android:targetSdkVersion="19" />
|
||||||
|
|
||||||
<supports-screens
|
<supports-screens
|
||||||
android:smallScreens="true"
|
android:smallScreens="true"
|
||||||
@@ -18,13 +18,15 @@
|
|||||||
<uses-feature android:name="android.hardware.telephony" android:required="false" />
|
<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.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<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.CHANGE_WIFI_MULTICAST_STATE" />
|
||||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" android:required="false"/>
|
<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.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_CONTACTS" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@@ -32,6 +34,11 @@
|
|||||||
android:label="KDE Connect"
|
android:label="KDE Connect"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:enabled="true"
|
||||||
|
android:name="org.kde.kdeconnect.BackgroundService">
|
||||||
|
</service>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:theme="@style/Theme.AppCompat"
|
android:theme="@style/Theme.AppCompat"
|
||||||
android:name="org.kde.kdeconnect.UserInterface.MainActivity"
|
android:name="org.kde.kdeconnect.UserInterface.MainActivity"
|
||||||
@@ -39,65 +46,56 @@
|
|||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</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
|
<activity
|
||||||
android:theme="@style/Theme.AppCompat"
|
android:theme="@style/Theme.AppCompat"
|
||||||
android:name="org.kde.kdeconnect.UserInterface.DeviceActivity"
|
android:name="org.kde.kdeconnect.UserInterface.DeviceActivity"
|
||||||
android:label="@string/device"
|
android:label="@string/device"
|
||||||
android:parentActivityName="org.kde.connect.UserInterface.MainActivity"
|
android:parentActivityName="org.kde.kdeconnect.UserInterface.MainActivity"
|
||||||
>
|
>
|
||||||
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="org.kde.connect.UserInterface.MainActivity" />
|
android:value="org.kde.kdeconnect.UserInterface.MainActivity" />
|
||||||
|
</activity>
|
||||||
</activity>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:theme="@style/Theme.AppCompat"
|
android:theme="@style/Theme.AppCompat"
|
||||||
android:name="org.kde.kdeconnect.UserInterface.PairActivity"
|
android:name="org.kde.kdeconnect.UserInterface.PairActivity"
|
||||||
android:label="@string/pair_device"
|
android:label="@string/pair_device"
|
||||||
android:parentActivityName="org.kde.connect.UserInterface.MainActivity"
|
android:parentActivityName="org.kde.kdeconnect.UserInterface.MainActivity"
|
||||||
>
|
>
|
||||||
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="org.kde.connect.UserInterface.MainActivity" />
|
android:value="org.kde.kdeconnect.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>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.kde.kdeconnect.UserInterface.SettingsActivity"
|
android:name="org.kde.kdeconnect.UserInterface.SettingsActivity"
|
||||||
android:label="@string/settings"
|
android:label="@string/settings"
|
||||||
>
|
android:parentActivityName="org.kde.kdeconnect.UserInterface.DeviceActivity"
|
||||||
|
>
|
||||||
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".DeviceActivity" />
|
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service
|
<activity
|
||||||
android:enabled="true"
|
android:name="org.kde.kdeconnect.UserInterface.PluginSettingsActivity"
|
||||||
android:name="org.kde.kdeconnect.BackgroundService">
|
android:label="@string/mpris_settings"
|
||||||
</service>
|
android:parentActivityName="org.kde.kdeconnect.UserInterface.SettingsActivity"
|
||||||
|
>
|
||||||
<service android:name="org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationReceiver"
|
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
android:value="org.kde.kdeconnect.UserInterface.SettingsActivity" />
|
||||||
<intent-filter>
|
</activity>
|
||||||
<action android:name="android.service.notification.NotificationListenerService" />
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<receiver android:name="org.kde.kdeconnect.KdeConnectBroadcastReceiver">
|
<receiver android:name="org.kde.kdeconnect.KdeConnectBroadcastReceiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@@ -124,6 +122,56 @@
|
|||||||
</intent-filter>-->
|
</intent-filter>-->
|
||||||
</receiver>
|
</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:windowSoftInputMode="stateHidden|adjustResize"
|
||||||
|
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||||
|
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>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@@ -1,18 +1,20 @@
|
|||||||
package org.kde.kdeconnect.ComputerLinks;
|
package org.kde.kdeconnect.Backends;
|
||||||
|
|
||||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
|
||||||
public abstract class BaseComputerLink {
|
public abstract class BaseLink {
|
||||||
|
|
||||||
private BaseLinkProvider linkProvider;
|
private final BaseLinkProvider linkProvider;
|
||||||
private String deviceId;
|
private final String deviceId;
|
||||||
private ArrayList<PackageReceiver> receivers = new ArrayList<PackageReceiver>();
|
private final ArrayList<PackageReceiver> receivers = new ArrayList<PackageReceiver>();
|
||||||
|
protected PrivateKey privateKey;
|
||||||
|
|
||||||
protected BaseComputerLink(String deviceId, BaseLinkProvider linkProvider) {
|
protected BaseLink(String deviceId, BaseLinkProvider linkProvider) {
|
||||||
this.linkProvider = linkProvider;
|
this.linkProvider = linkProvider;
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
}
|
}
|
||||||
@@ -21,6 +23,10 @@ public abstract class BaseComputerLink {
|
|||||||
return deviceId;
|
return deviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPrivateKey(PrivateKey key) {
|
||||||
|
privateKey = key;
|
||||||
|
}
|
||||||
|
|
||||||
public BaseLinkProvider getLinkProvider() {
|
public BaseLinkProvider getLinkProvider() {
|
||||||
return linkProvider;
|
return linkProvider;
|
||||||
}
|
}
|
||||||
@@ -44,7 +50,8 @@ public abstract class BaseComputerLink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TO OVERRIDE
|
//TO OVERRIDE, should be sync
|
||||||
public abstract boolean sendPackage(NetworkPackage np);
|
public abstract boolean sendPackage(NetworkPackage np);
|
||||||
|
public abstract boolean sendPackageEncrypted(NetworkPackage np, PublicKey key);
|
||||||
|
|
||||||
}
|
}
|
@@ -1,19 +1,19 @@
|
|||||||
package org.kde.kdeconnect.LinkProviders;
|
package org.kde.kdeconnect.Backends;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.kde.kdeconnect.ComputerLinks.BaseComputerLink;
|
import org.kde.kdeconnect.Backends.BaseLink;
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public abstract class BaseLinkProvider {
|
public abstract class BaseLinkProvider {
|
||||||
|
|
||||||
private ArrayList<ConnectionReceiver> connectionReceivers = new ArrayList<ConnectionReceiver>();
|
private final ArrayList<ConnectionReceiver> connectionReceivers = new ArrayList<ConnectionReceiver>();
|
||||||
|
|
||||||
public interface ConnectionReceiver {
|
public interface ConnectionReceiver {
|
||||||
public void onConnectionReceived(NetworkPackage identityPackage, BaseComputerLink link);
|
public void onConnectionReceived(NetworkPackage identityPackage, BaseLink link);
|
||||||
public void onConnectionLost(BaseComputerLink link);
|
public void onConnectionLost(BaseLink link);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addConnectionReceiver(ConnectionReceiver cr) {
|
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
|
//These two should be called when the provider links to a new computer
|
||||||
protected void connectionAccepted(NetworkPackage identityPackage, BaseComputerLink link) {
|
protected void connectionAccepted(NetworkPackage identityPackage, BaseLink link) {
|
||||||
Log.i("LinkProvider", "connectionAccepted");
|
Log.i("LinkProvider", "connectionAccepted");
|
||||||
for(ConnectionReceiver cr : connectionReceivers) {
|
for(ConnectionReceiver cr : connectionReceivers) {
|
||||||
cr.onConnectionReceived(identityPackage, link);
|
cr.onConnectionReceived(identityPackage, link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
protected void connectionLost(BaseComputerLink link) {
|
protected void connectionLost(BaseLink link) {
|
||||||
Log.i("LinkProvider", "connectionLost");
|
Log.i("LinkProvider", "connectionLost");
|
||||||
for(ConnectionReceiver cr : connectionReceivers) {
|
for(ConnectionReceiver cr : connectionReceivers) {
|
||||||
cr.onConnectionLost(link);
|
cr.onConnectionLost(link);
|
||||||
@@ -43,7 +43,7 @@ public abstract class BaseLinkProvider {
|
|||||||
public abstract void onStop();
|
public abstract void onStop();
|
||||||
public abstract void onNetworkChange();
|
public abstract void onNetworkChange();
|
||||||
|
|
||||||
public abstract int getPriority();
|
//public abstract int getPriority();
|
||||||
public abstract String getName();
|
public abstract String getName();
|
||||||
|
|
||||||
}
|
}
|
@@ -0,0 +1,201 @@
|
|||||||
|
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,8 +1,9 @@
|
|||||||
package org.kde.kdeconnect.LinkProviders;
|
package org.kde.kdeconnect.Backends.LanBackend;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.support.v4.util.LongSparseArray;
|
||||||
|
|
||||||
import org.apache.mina.core.future.ConnectFuture;
|
import org.apache.mina.core.future.ConnectFuture;
|
||||||
import org.apache.mina.core.future.IoFuture;
|
import org.apache.mina.core.future.IoFuture;
|
||||||
@@ -16,7 +17,7 @@ import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
|
|||||||
import org.apache.mina.transport.socket.nio.NioDatagramAcceptor;
|
import org.apache.mina.transport.socket.nio.NioDatagramAcceptor;
|
||||||
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
|
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
|
||||||
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||||
import org.kde.kdeconnect.ComputerLinks.LanComputerLink;
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
|
||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
@@ -30,9 +31,9 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
|
|
||||||
private final static int port = 1714;
|
private final static int port = 1714;
|
||||||
|
|
||||||
private Context context;
|
private final Context context;
|
||||||
private HashMap<String, LanComputerLink> visibleComputers = new HashMap<String, LanComputerLink>();
|
private final HashMap<String, LanLink> visibleComputers = new HashMap<String, LanLink>();
|
||||||
private HashMap<Long, LanComputerLink> nioSessions = new HashMap<Long, LanComputerLink>();
|
private final LongSparseArray<LanLink> nioSessions = new LongSparseArray<LanLink>();
|
||||||
|
|
||||||
private NioSocketAcceptor tcpAcceptor = null;
|
private NioSocketAcceptor tcpAcceptor = null;
|
||||||
private NioDatagramAcceptor udpAcceptor = null;
|
private NioDatagramAcceptor udpAcceptor = null;
|
||||||
@@ -41,8 +42,9 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
@Override
|
@Override
|
||||||
public void sessionClosed(IoSession session) throws Exception {
|
public void sessionClosed(IoSession session) throws Exception {
|
||||||
|
|
||||||
LanComputerLink brokenLink = nioSessions.remove(session.getId());
|
LanLink brokenLink = nioSessions.get(session.getId());
|
||||||
if (brokenLink != null) {
|
if (brokenLink != null) {
|
||||||
|
nioSessions.remove(session.getId());
|
||||||
connectionLost(brokenLink);
|
connectionLost(brokenLink);
|
||||||
brokenLink.disconnect();
|
brokenLink.disconnect();
|
||||||
String deviceId = brokenLink.getDeviceId();
|
String deviceId = brokenLink.getDeviceId();
|
||||||
@@ -58,21 +60,30 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
super.messageReceived(session, message);
|
super.messageReceived(session, message);
|
||||||
|
|
||||||
//Log.e("LanLinkProvider","Incoming package, address: "+session.getRemoteAddress()).toString());
|
//Log.e("LanLinkProvider","Incoming package, address: "+session.getRemoteAddress()).toString());
|
||||||
|
//Log.e("LanLinkProvider","Received:"+message);
|
||||||
|
|
||||||
String theMessage = (String) message;
|
String theMessage = (String) message;
|
||||||
|
if (theMessage.isEmpty()) {
|
||||||
|
Log.e("LanLinkProvider","Empty package received");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
NetworkPackage np = NetworkPackage.unserialize(theMessage);
|
NetworkPackage np = NetworkPackage.unserialize(theMessage);
|
||||||
|
|
||||||
LanComputerLink prevLink = nioSessions.get(session.getId());
|
|
||||||
|
|
||||||
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
|
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
|
||||||
|
|
||||||
String myId = NetworkPackage.createIdentityPackage(context).getString("deviceId");
|
String myId = NetworkPackage.createIdentityPackage(context).getString("deviceId");
|
||||||
if (np.getString("deviceId").equals(myId)) {
|
if (np.getString("deviceId").equals(myId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LanComputerLink link = new LanComputerLink(session, np.getString("deviceId"), LanLinkProvider.this);
|
|
||||||
|
//Log.e("LanLinkProvider", "Identity package received from "+np.getString("deviceName"));
|
||||||
|
|
||||||
|
LanLink link = new LanLink(session, np.getString("deviceId"), LanLinkProvider.this);
|
||||||
nioSessions.put(session.getId(),link);
|
nioSessions.put(session.getId(),link);
|
||||||
addLink(np, link);
|
addLink(np, link);
|
||||||
} else {
|
} else {
|
||||||
|
LanLink prevLink = nioSessions.get(session.getId());
|
||||||
if (prevLink == null) {
|
if (prevLink == null) {
|
||||||
Log.e("LanLinkProvider","2 Expecting an identity package");
|
Log.e("LanLinkProvider","2 Expecting an identity package");
|
||||||
} else {
|
} else {
|
||||||
@@ -83,84 +94,83 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private IoHandler udpHandler = new IoHandlerAdapter() {
|
private final IoHandler udpHandler = new IoHandlerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(IoSession udpSession, Object message) throws Exception {
|
public void messageReceived(IoSession udpSession, Object message) throws Exception {
|
||||||
super.messageReceived(udpSession, message);
|
super.messageReceived(udpSession, message);
|
||||||
|
|
||||||
//Log.e("LanLinkProvider", "Udp message received (" + message.getClass() + ") " + message.toString());
|
//Log.e("LanLinkProvider", "Udp message received (" + message.getClass() + ") " + message.toString());
|
||||||
|
|
||||||
NetworkPackage np = null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//We should receive a string thanks to the TextLineCodecFactory filter
|
//We should receive a string thanks to the TextLineCodecFactory filter
|
||||||
String theMessage = (String) message;
|
String theMessage = (String) message;
|
||||||
np = NetworkPackage.unserialize(theMessage);
|
final NetworkPackage identityPackage = NetworkPackage.unserialize(theMessage);
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Log.e("LanLinkProvider", "Could not unserialize package");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (np != null) {
|
if (!identityPackage.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
|
||||||
|
|
||||||
final NetworkPackage identityPackage = np;
|
|
||||||
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
|
|
||||||
Log.e("LanLinkProvider", "1 Expecting an identity package");
|
Log.e("LanLinkProvider", "1 Expecting an identity package");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
String myId = NetworkPackage.createIdentityPackage(context).getString("deviceId");
|
String myId = NetworkPackage.createIdentityPackage(context).getString("deviceId");
|
||||||
if (np.getString("deviceId").equals(myId)) {
|
if (identityPackage.getString("deviceId").equals(myId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i("LanLinkProvider", "Identity package received, creating link");
|
Log.i("LanLinkProvider", "Identity package received, creating link");
|
||||||
|
|
||||||
try {
|
final InetSocketAddress address = (InetSocketAddress) udpSession.getRemoteAddress();
|
||||||
final InetSocketAddress address = (InetSocketAddress) udpSession.getRemoteAddress();
|
|
||||||
|
|
||||||
final NioSocketConnector connector = new NioSocketConnector();
|
final NioSocketConnector connector = new NioSocketConnector();
|
||||||
connector.setHandler(tcpHandler);
|
connector.setHandler(tcpHandler);
|
||||||
//TextLineCodecFactory will split incoming data delimited by the given string
|
//TextLineCodecFactory will split incoming data delimited by the given string
|
||||||
connector.getFilterChain().addLast("codec",
|
connector.getFilterChain().addLast("codec",
|
||||||
new ProtocolCodecFilter(
|
new ProtocolCodecFilter(
|
||||||
new TextLineCodecFactory(Charset.defaultCharset(), LineDelimiter.UNIX, LineDelimiter.UNIX)
|
new TextLineCodecFactory(Charset.defaultCharset(), LineDelimiter.UNIX, LineDelimiter.UNIX)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
connector.getSessionConfig().setKeepAlive(true);
|
connector.getSessionConfig().setKeepAlive(true);
|
||||||
|
|
||||||
int tcpPort = np.getInt("tcpPort",port);
|
int tcpPort = identityPackage.getInt("tcpPort",port);
|
||||||
ConnectFuture future = connector.connect(new InetSocketAddress(address.getAddress(), tcpPort));
|
ConnectFuture future = connector.connect(new InetSocketAddress(address.getAddress(), tcpPort));
|
||||||
future.addListener(new IoFutureListener<IoFuture>() {
|
future.addListener(new IoFutureListener<IoFuture>() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(IoFuture ioFuture) {
|
public void operationComplete(IoFuture ioFuture) {
|
||||||
IoSession session = ioFuture.getSession();
|
final IoSession session = ioFuture.getSession();
|
||||||
|
|
||||||
Log.i("LanLinkProvider", "Connection successful: " + session.isConnected());
|
final LanLink link = new LanLink(session, identityPackage.getString("deviceId"), LanLinkProvider.this);
|
||||||
|
|
||||||
LanComputerLink link = new LanComputerLink(session, identityPackage.getString("deviceId"), LanLinkProvider.this);
|
Log.i("LanLinkProvider", "Connection successful: " + session.isConnected());
|
||||||
|
|
||||||
NetworkPackage np2 = NetworkPackage.createIdentityPackage(context);
|
new Thread(new Runnable() {
|
||||||
link.sendPackage(np2);
|
@Override
|
||||||
|
public void run() {
|
||||||
|
NetworkPackage np2 = NetworkPackage.createIdentityPackage(context);
|
||||||
|
link.sendPackage(np2);
|
||||||
|
|
||||||
nioSessions.put(session.getId(), link);
|
nioSessions.put(session.getId(), link);
|
||||||
addLink(identityPackage, link);
|
addLink(identityPackage, link);
|
||||||
}
|
}
|
||||||
});
|
}).start();
|
||||||
|
|
||||||
} 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, LanComputerLink link) {
|
private void addLink(NetworkPackage identityPackage, LanLink link) {
|
||||||
String deviceId = identityPackage.getString("deviceId");
|
String deviceId = identityPackage.getString("deviceId");
|
||||||
Log.i("LanLinkProvider","addLink to "+deviceId);
|
Log.i("LanLinkProvider","addLink to "+deviceId);
|
||||||
LanComputerLink oldLink = visibleComputers.get(deviceId);
|
LanLink oldLink = visibleComputers.get(deviceId);
|
||||||
|
if (oldLink == link) {
|
||||||
|
Log.e("KDEConnect", "LanLinkProvider: oldLink == link. This should not happen!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
visibleComputers.put(deviceId, link);
|
visibleComputers.put(deviceId, link);
|
||||||
connectionAccepted(identityPackage, link);
|
connectionAccepted(identityPackage, link);
|
||||||
if (oldLink != null) {
|
if (oldLink != null) {
|
||||||
@@ -257,25 +267,21 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNetworkChange() {
|
public void onNetworkChange() {
|
||||||
|
|
||||||
onStop();
|
onStop();
|
||||||
onStart();
|
onStart();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
|
|
||||||
udpAcceptor.unbind();
|
udpAcceptor.unbind();
|
||||||
tcpAcceptor.unbind();
|
tcpAcceptor.unbind();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
@Override
|
@Override
|
||||||
public int getPriority() {
|
public int getPriority() {
|
||||||
return 1000;
|
return 1000;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "LanLinkProvider";
|
return "LanLinkProvider";
|
@@ -0,0 +1,43 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,40 @@
|
|||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
@@ -11,10 +11,10 @@ import android.preference.PreferenceManager;
|
|||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.kde.kdeconnect.ComputerLinks.BaseComputerLink;
|
import org.kde.kdeconnect.Backends.BaseLink;
|
||||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
import org.kde.kdeconnect.LinkProviders.LanLinkProvider;
|
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
|
||||||
import org.kde.kdeconnect.LinkProviders.LoopbackLinkProvider;
|
import org.kde.kdeconnect.UserInterface.MainSettingsActivity;
|
||||||
|
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.KeyPairGenerator;
|
import java.security.KeyPairGenerator;
|
||||||
@@ -26,11 +26,11 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
|
|
||||||
public class BackgroundService extends Service {
|
public class BackgroundService extends Service {
|
||||||
|
|
||||||
private ArrayList<BaseLinkProvider> linkProviders = new ArrayList<BaseLinkProvider>();
|
private final ArrayList<BaseLinkProvider> linkProviders = new ArrayList<BaseLinkProvider>();
|
||||||
|
|
||||||
private HashMap<String, Device> devices = new HashMap<String, Device>();
|
private final HashMap<String, Device> devices = new HashMap<String, Device>();
|
||||||
|
|
||||||
private Device.PairingCallback devicePairingCallback = new Device.PairingCallback() {
|
private final Device.PairingCallback devicePairingCallback = new Device.PairingCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void incomingRequest() {
|
public void incomingRequest() {
|
||||||
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged();
|
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged();
|
||||||
@@ -50,18 +50,20 @@ public class BackgroundService extends Service {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private void loadRememberedDevicesFromSettings() {
|
private void loadRememberedDevicesFromSettings() {
|
||||||
|
//Log.e("BackgroundService", "Loading remembered trusted devices");
|
||||||
SharedPreferences preferences = getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
SharedPreferences preferences = getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||||
Set<String> trustedDevices = preferences.getAll().keySet();
|
Set<String> trustedDevices = preferences.getAll().keySet();
|
||||||
for(String deviceId : trustedDevices) {
|
for(String deviceId : trustedDevices) {
|
||||||
|
//Log.e("BackgroundService", "Loading device "+deviceId);
|
||||||
if (preferences.getBoolean(deviceId, false)) {
|
if (preferences.getBoolean(deviceId, false)) {
|
||||||
Device device = new Device(getBaseContext(), deviceId);
|
Device device = new Device(this, deviceId);
|
||||||
devices.put(deviceId,device);
|
devices.put(deviceId,device);
|
||||||
device.addPairingCallback(devicePairingCallback);
|
device.addPairingCallback(devicePairingCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerLinkProviders() {
|
private void registerLinkProviders() {
|
||||||
|
|
||||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
|
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
|
||||||
@@ -79,9 +81,9 @@ public class BackgroundService extends Service {
|
|||||||
return devices.get(id);
|
return devices.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
|
private final BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionReceived(final NetworkPackage identityPackage, final BaseComputerLink link) {
|
public void onConnectionReceived(final NetworkPackage identityPackage, final BaseLink link) {
|
||||||
|
|
||||||
Log.i("BackgroundService", "Connection accepted!");
|
Log.i("BackgroundService", "Connection accepted!");
|
||||||
|
|
||||||
@@ -91,11 +93,10 @@ public class BackgroundService extends Service {
|
|||||||
|
|
||||||
if (device != null) {
|
if (device != null) {
|
||||||
Log.i("BackgroundService", "addLink, known device: " + deviceId);
|
Log.i("BackgroundService", "addLink, known device: " + deviceId);
|
||||||
device.addLink(link);
|
device.addLink(identityPackage, link);
|
||||||
} else {
|
} else {
|
||||||
Log.i("BackgroundService", "addLink,unknown device: " + deviceId);
|
Log.i("BackgroundService", "addLink,unknown device: " + deviceId);
|
||||||
String name = identityPackage.getString("deviceName");
|
device = new Device(BackgroundService.this, identityPackage, link);
|
||||||
device = new Device(getBaseContext(), deviceId, name, link);
|
|
||||||
devices.put(deviceId, device);
|
devices.put(deviceId, device);
|
||||||
device.addPairingCallback(devicePairingCallback);
|
device.addPairingCallback(devicePairingCallback);
|
||||||
}
|
}
|
||||||
@@ -104,12 +105,13 @@ public class BackgroundService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionLost(BaseComputerLink link) {
|
public void onConnectionLost(BaseLink link) {
|
||||||
Device d = devices.get(link.getDeviceId());
|
Device d = devices.get(link.getDeviceId());
|
||||||
Log.i("onConnectionLost", "removeLink, deviceId: " + link.getDeviceId());
|
Log.i("onConnectionLost", "removeLink, deviceId: " + link.getDeviceId());
|
||||||
if (d != null) {
|
if (d != null) {
|
||||||
d.removeLink(link);
|
d.removeLink(link);
|
||||||
if (!d.isReachable() && !d.isPaired()) {
|
if (!d.isReachable() && !d.isPaired()) {
|
||||||
|
//Log.e("onConnectionLost","Removing connection device because it was not paired");
|
||||||
devices.remove(link.getDeviceId());
|
devices.remove(link.getDeviceId());
|
||||||
d.removePairingCallback(devicePairingCallback);
|
d.removePairingCallback(devicePairingCallback);
|
||||||
}
|
}
|
||||||
@@ -179,6 +181,7 @@ public class BackgroundService extends Service {
|
|||||||
Log.i("BackgroundService","Service not started yet, initializing...");
|
Log.i("BackgroundService","Service not started yet, initializing...");
|
||||||
|
|
||||||
initializeRsaKeys();
|
initializeRsaKeys();
|
||||||
|
MainSettingsActivity.initializeDeviceName(this);
|
||||||
loadRememberedDevicesFromSettings();
|
loadRememberedDevicesFromSettings();
|
||||||
registerLinkProviders();
|
registerLinkProviders();
|
||||||
|
|
||||||
@@ -262,14 +265,13 @@ public class BackgroundService extends Service {
|
|||||||
void onServiceStart(BackgroundService service);
|
void onServiceStart(BackgroundService service);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
|
private final static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
|
||||||
|
|
||||||
private static final Lock mutex = new ReentrantLock(true);
|
private final static Lock mutex = new ReentrantLock(true);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
//This will be called for each intent launch, even if the service is already started and it is reused
|
//This will be called for each intent launch, even if the service is already started and it is reused
|
||||||
Log.i("BackgroundService","onStartCommand");
|
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
for (InstanceCallback c : callbacks) {
|
for (InstanceCallback c : callbacks) {
|
||||||
c.onServiceStart(this);
|
c.onServiceStart(this);
|
@@ -7,7 +7,6 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
@@ -15,7 +14,7 @@ import android.support.v4.app.NotificationCompat;
|
|||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.kde.kdeconnect.ComputerLinks.BaseComputerLink;
|
import org.kde.kdeconnect.Backends.BaseLink;
|
||||||
import org.kde.kdeconnect.Plugins.Plugin;
|
import org.kde.kdeconnect.Plugins.Plugin;
|
||||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||||
import org.kde.kdeconnect.UserInterface.PairActivity;
|
import org.kde.kdeconnect.UserInterface.PairActivity;
|
||||||
@@ -34,14 +33,15 @@ import java.util.Set;
|
|||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
public class Device implements BaseComputerLink.PackageReceiver {
|
public class Device implements BaseLink.PackageReceiver {
|
||||||
|
|
||||||
private Context context;
|
private final Context context;
|
||||||
|
|
||||||
private String deviceId;
|
private final String deviceId;
|
||||||
private String name;
|
private final String name;
|
||||||
private PublicKey publicKey;
|
public PublicKey publicKey;
|
||||||
private int notificationId;
|
private int notificationId;
|
||||||
|
private int protocolVersion;
|
||||||
|
|
||||||
private enum PairStatus {
|
private enum PairStatus {
|
||||||
NotPaired,
|
NotPaired,
|
||||||
@@ -61,11 +61,11 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
private ArrayList<PairingCallback> pairingCallback = new ArrayList<PairingCallback>();
|
private ArrayList<PairingCallback> pairingCallback = new ArrayList<PairingCallback>();
|
||||||
private Timer pairingTimer;
|
private Timer pairingTimer;
|
||||||
|
|
||||||
private ArrayList<BaseComputerLink> links = new ArrayList<BaseComputerLink>();
|
private final ArrayList<BaseLink> links = new ArrayList<BaseLink>();
|
||||||
private HashMap<String, Plugin> plugins = new HashMap<String, Plugin>();
|
private final HashMap<String, Plugin> plugins = new HashMap<String, Plugin>();
|
||||||
private HashMap<String, Plugin> failedPlugins = new HashMap<String, Plugin>();
|
private final HashMap<String, Plugin> failedPlugins = new HashMap<String, Plugin>();
|
||||||
|
|
||||||
SharedPreferences settings;
|
private final SharedPreferences settings;
|
||||||
|
|
||||||
//Remembered trusted device, we need to wait for a incoming devicelink to communicate
|
//Remembered trusted device, we need to wait for a incoming devicelink to communicate
|
||||||
Device(Context context, String deviceId) {
|
Device(Context context, String deviceId) {
|
||||||
@@ -77,6 +77,7 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.name = settings.getString("deviceName", "unknown device");
|
this.name = settings.getString("deviceName", "unknown device");
|
||||||
this.pairStatus = PairStatus.Paired;
|
this.pairStatus = PairStatus.Paired;
|
||||||
|
this.protocolVersion = NetworkPackage.ProtocolVersion; //We don't know it yet
|
||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0);
|
byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0);
|
||||||
@@ -90,18 +91,20 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Device known via an incoming connection sent to us via a devicelink, we know everything but we don't trust it yet
|
//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, String deviceId, String name, BaseComputerLink dl) {
|
Device(Context context, NetworkPackage np, BaseLink dl) {
|
||||||
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
//Log.e("Device","Constructor B");
|
//Log.e("Device","Constructor B");
|
||||||
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.deviceId = deviceId;
|
this.deviceId = np.getString("deviceId");
|
||||||
this.name = name;
|
this.name = np.getString("deviceName", "unidentified device");
|
||||||
|
this.protocolVersion = np.getInt("protocolVersion");
|
||||||
this.pairStatus = PairStatus.NotPaired;
|
this.pairStatus = PairStatus.NotPaired;
|
||||||
this.publicKey = null;
|
this.publicKey = null;
|
||||||
|
|
||||||
addLink(dl);
|
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
addLink(np, dl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@@ -112,6 +115,10 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
return deviceId;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -144,37 +151,55 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
Resources res = context.getResources();
|
Resources res = context.getResources();
|
||||||
|
|
||||||
if (pairStatus == PairStatus.Paired) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if (pairStatus == PairStatus.Requested) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if (!isReachable()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Send our own public key
|
//Send our own public key
|
||||||
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
|
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
|
||||||
boolean success = sendPackage(np);
|
sendPackage(np, new SendPackageFinishedCallback(){
|
||||||
|
|
||||||
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
|
@Override
|
||||||
public void run() {
|
public void sendSuccessful() {
|
||||||
for (PairingCallback cb : pairingCallback) cb.pairingFailed(context.getString(R.string.error_timed_out));
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)");
|
||||||
pairStatus = PairStatus.NotPaired;
|
pairStatus = PairStatus.NotPaired;
|
||||||
}
|
}
|
||||||
}, 20*1000);
|
|
||||||
|
|
||||||
pairStatus = PairStatus.Requested;
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,8 +209,7 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
|
|
||||||
public void unpair() {
|
public void unpair() {
|
||||||
|
|
||||||
if (!isPaired()) return;
|
//Log.e("Device","Unpairing (unpair)");
|
||||||
|
|
||||||
pairStatus = PairStatus.NotPaired;
|
pairStatus = PairStatus.NotPaired;
|
||||||
|
|
||||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||||
@@ -201,46 +225,70 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void acceptPairing() {
|
private void pairingDone() {
|
||||||
|
|
||||||
Log.i("Device","Accepted pairing");
|
//Log.e("Device", "Storing as trusted, deviceId: "+deviceId);
|
||||||
|
|
||||||
//Send our own public key
|
if (pairingTimer != null) pairingTimer.cancel();
|
||||||
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
|
|
||||||
boolean success = sendPackage(np);
|
|
||||||
|
|
||||||
if (!success) return;
|
|
||||||
|
|
||||||
pairStatus = PairStatus.Paired;
|
pairStatus = PairStatus.Paired;
|
||||||
|
|
||||||
//Store as trusted device
|
//Store as trusted device
|
||||||
String encodedPublicKey = Base64.encodeToString(publicKey.getEncoded(), 0);
|
|
||||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||||
preferences.edit().putBoolean(deviceId,true).commit();
|
preferences.edit().putBoolean(deviceId,true).commit();
|
||||||
|
|
||||||
//Store device information needed to create a Device object in a future
|
//Store device information needed to create a Device object in a future
|
||||||
SharedPreferences.Editor editor = settings.edit();
|
SharedPreferences.Editor editor = settings.edit();
|
||||||
editor.putString("deviceName", getName());
|
editor.putString("deviceName", getName());
|
||||||
|
String encodedPublicKey = Base64.encodeToString(publicKey.getEncoded(), 0);
|
||||||
editor.putString("publicKey", encodedPublicKey);
|
editor.putString("publicKey", encodedPublicKey);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
reloadPluginsFromSettings();
|
reloadPluginsFromSettings();
|
||||||
|
|
||||||
for (PairingCallback cb : pairingCallback) cb.pairingSuccessful();
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void rejectPairing() {
|
public void rejectPairing() {
|
||||||
|
|
||||||
Log.i("Device","Rejected pairing");
|
Log.i("Device","Rejected pair request started by the other device");
|
||||||
|
|
||||||
|
//Log.e("Device","Unpairing (rejectPairing)");
|
||||||
pairStatus = PairStatus.NotPaired;
|
pairStatus = PairStatus.NotPaired;
|
||||||
|
|
||||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
|
||||||
np.set("pair", false);
|
np.set("pair", false);
|
||||||
sendPackage(np);
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,18 +303,32 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
return !links.isEmpty();
|
return !links.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addLink(BaseComputerLink link) {
|
public void addLink(NetworkPackage identityPackage, BaseLink link) {
|
||||||
|
|
||||||
|
this.protocolVersion = identityPackage.getInt("protocolVersion");
|
||||||
|
|
||||||
links.add(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());
|
Log.i("Device","addLink "+link.getLinkProvider().getName()+" -> "+getName() + " active links: "+ links.size());
|
||||||
|
|
||||||
Collections.sort(links, new Comparator<BaseComputerLink>() {
|
/*
|
||||||
|
Collections.sort(links, new Comparator<BaseLink>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(BaseComputerLink o, BaseComputerLink o2) {
|
public int compare(BaseLink o, BaseLink o2) {
|
||||||
return o2.getLinkProvider().getPriority() - o.getLinkProvider().getPriority();
|
return o2.getLinkProvider().getPriority() - o.getLinkProvider().getPriority();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
link.addPackageReceiver(this);
|
link.addPackageReceiver(this);
|
||||||
|
|
||||||
@@ -275,7 +337,7 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeLink(BaseComputerLink link) {
|
public void removeLink(BaseLink link) {
|
||||||
link.removePackageReceiver(this);
|
link.removePackageReceiver(this);
|
||||||
links.remove(link);
|
links.remove(link);
|
||||||
Log.i("Device","removeLink: "+link.getLinkProvider().getName() + " -> "+getName() + " active links: "+ links.size());
|
Log.i("Device","removeLink: "+link.getLinkProvider().getName() + " -> "+getName() + " active links: "+ links.size());
|
||||||
@@ -295,9 +357,12 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
|
|
||||||
if (wantsPair == isPaired()) {
|
if (wantsPair == isPaired()) {
|
||||||
if (pairStatus == PairStatus.Requested) {
|
if (pairStatus == PairStatus.Requested) {
|
||||||
|
//Log.e("Device","Unpairing (pair rejected)");
|
||||||
pairStatus = PairStatus.NotPaired;
|
pairStatus = PairStatus.NotPaired;
|
||||||
pairingTimer.cancel();
|
if (pairingTimer != null) 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -312,7 +377,9 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Log.e("Device","Pairing exception: Received incorrect key");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,23 +387,9 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
|
|
||||||
Log.i("Pairing","Pair answer");
|
Log.i("Pairing","Pair answer");
|
||||||
|
|
||||||
pairStatus = PairStatus.Paired;
|
if (pairingTimer != null) pairingTimer.cancel();
|
||||||
pairingTimer.cancel();
|
|
||||||
|
|
||||||
//Store as trusted device
|
pairingDone();
|
||||||
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 {
|
} else {
|
||||||
|
|
||||||
@@ -355,7 +408,7 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
.setTicker(res.getString(R.string.pair_requested))
|
.setTicker(res.getString(R.string.pair_requested))
|
||||||
.setSmallIcon(android.R.drawable.ic_menu_help)
|
.setSmallIcon(android.R.drawable.ic_menu_help)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setDefaults(Notification.DEFAULT_SOUND)
|
.setDefaults(Notification.DEFAULT_ALL)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
@@ -363,15 +416,17 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
notificationId = (int)System.currentTimeMillis();
|
notificationId = (int)System.currentTimeMillis();
|
||||||
notificationManager.notify(notificationId, noti);
|
notificationManager.notify(notificationId, noti);
|
||||||
|
|
||||||
|
if (pairingTimer != null) pairingTimer.cancel();
|
||||||
pairingTimer = new Timer();
|
pairingTimer = new Timer();
|
||||||
|
|
||||||
pairingTimer.schedule(new TimerTask() {
|
pairingTimer.schedule(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
Log.e("Device","Unpairing (timeout B)");
|
||||||
pairStatus = PairStatus.NotPaired;
|
pairStatus = PairStatus.NotPaired;
|
||||||
notificationManager.cancel(notificationId);
|
notificationManager.cancel(notificationId);
|
||||||
}
|
}
|
||||||
}, 19*1000); //Time to show notification
|
}, 25*1000); //Time to show notification, waiting for user to accept (peer will timeout in 30 seconds)
|
||||||
|
|
||||||
pairStatus = PairStatus.RequestedByPeer;
|
pairStatus = PairStatus.RequestedByPeer;
|
||||||
for (PairingCallback cb : pairingCallback) cb.incomingRequest();
|
for (PairingCallback cb : pairingCallback) cb.incomingRequest();
|
||||||
|
|
||||||
@@ -381,78 +436,97 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
|
|
||||||
if (pairStatus == PairStatus.Requested) {
|
if (pairStatus == PairStatus.Requested) {
|
||||||
pairingTimer.cancel();
|
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) {
|
} else if (pairStatus == PairStatus.Paired) {
|
||||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||||
preferences.edit().remove(deviceId).commit();
|
preferences.edit().remove(deviceId).commit();
|
||||||
reloadPluginsFromSettings();
|
reloadPluginsFromSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Log.e("Device","Unpairing (unpair request)");
|
||||||
pairStatus = PairStatus.NotPaired;
|
pairStatus = PairStatus.NotPaired;
|
||||||
for (PairingCallback cb : pairingCallback) cb.unpaired();
|
for (PairingCallback cb : pairingCallback) cb.unpaired();
|
||||||
|
|
||||||
}
|
}
|
||||||
} else if (!isPaired()) {
|
} else if (!isPaired()) {
|
||||||
|
|
||||||
//TODO: Notify the other side that we don't trust them
|
unpair();
|
||||||
Log.e("onPackageReceived","Device not paired, ignoring package!");
|
Log.e("onPackageReceived","Device not paired, ignoring package!");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_ENCRYPTED)) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
//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("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()) {
|
for (Plugin plugin : plugins.values()) {
|
||||||
plugin.onPackageReceived(np);
|
try {
|
||||||
|
plugin.onPackageReceived(np);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("Device", "Exception in "+plugin.getDisplayName()+"'s onPackageReceived()");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface SendPackageFinishedCallback {
|
||||||
|
void sendSuccessful();
|
||||||
|
void sendFailed();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean sendPackage(final NetworkPackage np) {
|
public void sendPackage(NetworkPackage np) {
|
||||||
|
sendPackage(np,null);
|
||||||
|
}
|
||||||
|
|
||||||
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR) && isPaired()) {
|
//Async
|
||||||
try {
|
public void sendPackage(final NetworkPackage np, final SendPackageFinishedCallback callback) {
|
||||||
np.encrypt(publicKey);
|
|
||||||
} catch(Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Log.e("Device","sendPackage exception - could not encrypt");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new AsyncTask<Void,Void,Void>() {
|
|
||||||
|
final Exception backtrace = new Exception();
|
||||||
|
new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... voids) {
|
public void run() {
|
||||||
for(BaseComputerLink link : links) {
|
|
||||||
//Log.e("sendPackage","Trying "+link.getLinkProvider().getName());
|
//Log.e("sendPackage", "Sending package...");
|
||||||
if (link.sendPackage(np)) {
|
//Log.e("sendPackage", np.serialize());
|
||||||
//Log.e("sent using", link.getLinkProvider().getName());
|
|
||||||
return null;
|
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;
|
||||||
}
|
}
|
||||||
Log.e("sendPackage","Error: Package could not be sent ("+links.size()+" links available)");
|
|
||||||
return null;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}.execute();
|
}).start();
|
||||||
|
|
||||||
//TODO: Detect when unable to send a package and try again somehow
|
|
||||||
|
|
||||||
return !links.isEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -467,7 +541,7 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
return plugins.get(name);
|
return plugins.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPlugin(final String name) {
|
private synchronized void addPlugin(final String name) {
|
||||||
Plugin existing = plugins.get(name);
|
Plugin existing = plugins.get(name);
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
Log.w("addPlugin","plugin already present:" + name);
|
Log.w("addPlugin","plugin already present:" + name);
|
||||||
@@ -485,24 +559,25 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
|
boolean success;
|
||||||
try {
|
try {
|
||||||
boolean success = plugin.onCreate();
|
success = plugin.onCreate();
|
||||||
if (!success) {
|
|
||||||
Log.e("addPlugin", "plugin failed to load " + name);
|
|
||||||
failedPlugins.put(name, plugin);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
failedPlugins.put(name, plugin);
|
success = false;
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Log.e("addPlugin", "Exception loading plugin " + name);
|
Log.e("addPlugin", "Exception loading plugin " + name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Log.e("addPlugin","added " + name);
|
if (success) {
|
||||||
|
//Log.e("addPlugin","added " + name);
|
||||||
failedPlugins.remove(name);
|
failedPlugins.remove(name);
|
||||||
plugins.put(name, plugin);
|
plugins.put(name, plugin);
|
||||||
|
} else {
|
||||||
|
Log.e("addPlugin", "plugin failed to load " + name);
|
||||||
|
plugins.remove(name);
|
||||||
|
failedPlugins.put(name, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
for (PluginsChangedListener listener : pluginsChangedListeners) {
|
for (PluginsChangedListener listener : pluginsChangedListeners) {
|
||||||
listener.onPluginsChanged(Device.this);
|
listener.onPluginsChanged(Device.this);
|
||||||
@@ -513,13 +588,14 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean removePlugin(String name) {
|
private synchronized boolean removePlugin(String name) {
|
||||||
|
|
||||||
Plugin plugin = plugins.remove(name);
|
Plugin plugin = plugins.remove(name);
|
||||||
Plugin failedPlugin = failedPlugins.remove(name);
|
Plugin failedPlugin = failedPlugins.remove(name);
|
||||||
|
|
||||||
if (plugin == null) {
|
if (plugin == null) {
|
||||||
if (failedPlugin == null) {
|
if (failedPlugin == null) {
|
||||||
|
//Not found
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
plugin = failedPlugin;
|
plugin = failedPlugin;
|
||||||
@@ -527,25 +603,22 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
plugin.onDestroy();
|
plugin.onDestroy();
|
||||||
|
//Log.e("removePlugin","removed " + name);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Log.e("removePlugin","Exception calling onDestroy for plugin "+name);
|
Log.e("removePlugin","Exception calling onDestroy for plugin "+name);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Log.e("removePlugin","removed " + name);
|
|
||||||
|
|
||||||
for (PluginsChangedListener listener : pluginsChangedListeners) {
|
for (PluginsChangedListener listener : pluginsChangedListeners) {
|
||||||
listener.onPluginsChanged(this);
|
listener.onPluginsChanged(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPluginEnabled(String pluginName, boolean value) {
|
public void setPluginEnabled(String pluginName, boolean value) {
|
||||||
settings.edit().putBoolean(pluginName,value).commit();
|
settings.edit().putBoolean(pluginName,value).commit();
|
||||||
if (value) addPlugin(pluginName);
|
if (value && isPaired() && isReachable()) addPlugin(pluginName);
|
||||||
else removePlugin(pluginName);
|
else removePlugin(pluginName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,6 +628,9 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasPluginsLoaded() {
|
||||||
|
return !plugins.isEmpty() || !failedPlugins.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
public void reloadPluginsFromSettings() {
|
public void reloadPluginsFromSettings() {
|
||||||
|
|
||||||
@@ -574,9 +650,7 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PluginsChangedListener listener : pluginsChangedListeners) {
|
//No need to call PluginsChangedListeners because addPlugin and removePlugin already do so
|
||||||
listener.onPluginsChanged(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashMap<String,Plugin> getLoadedPlugins() {
|
public HashMap<String,Plugin> getLoadedPlugins() {
|
||||||
@@ -591,7 +665,7 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
void onPluginsChanged(Device device);
|
void onPluginsChanged(Device device);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<PluginsChangedListener> pluginsChangedListeners = new ArrayList<PluginsChangedListener>();
|
private final ArrayList<PluginsChangedListener> pluginsChangedListeners = new ArrayList<PluginsChangedListener>();
|
||||||
|
|
||||||
public void addPluginsChangedListener(PluginsChangedListener listener) {
|
public void addPluginsChangedListener(PluginsChangedListener listener) {
|
||||||
pluginsChangedListeners.add(listener);
|
pluginsChangedListeners.add(listener);
|
@@ -13,7 +13,7 @@ public class AppsHelper {
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
PackageManager pm = context.getPackageManager();
|
PackageManager pm = context.getPackageManager();
|
||||||
ApplicationInfo ai = pm.getApplicationInfo( packageName, 0);
|
ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
|
||||||
|
|
||||||
return pm.getApplicationLabel(ai).toString();
|
return pm.getApplicationLabel(ai).toString();
|
||||||
|
|
@@ -3,9 +3,7 @@ package org.kde.kdeconnect.Helpers;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.ContactsContract;
|
|
||||||
import android.provider.ContactsContract.PhoneLookup;
|
import android.provider.ContactsContract.PhoneLookup;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
public class ContactsHelper {
|
public class ContactsHelper {
|
||||||
|
|
||||||
@@ -31,14 +29,17 @@ public class ContactsHelper {
|
|||||||
|
|
||||||
// Take the first match only
|
// Take the first match only
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
int nameIndex = cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME);
|
int nameIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME);
|
||||||
if (nameIndex != -1) {
|
if (nameIndex != -1) {
|
||||||
String name = cursor.getString(nameIndex);
|
String name = cursor.getString(nameIndex);
|
||||||
//Log.e("PhoneNumberLookup", "success: " + name);
|
//Log.e("PhoneNumberLookup", "success: " + name);
|
||||||
return name;
|
cursor.close();
|
||||||
|
return name + " (" + number + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cursor != null) cursor.close();
|
||||||
|
|
||||||
return number;
|
return number;
|
||||||
|
|
||||||
}
|
}
|
@@ -1,15 +1,17 @@
|
|||||||
package org.kde.kdeconnect;
|
package org.kde.kdeconnect.Helpers;
|
||||||
|
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class HumanDeviceNames {
|
public class DeviceHelper {
|
||||||
|
|
||||||
//from https://github.com/meetup/android-device-names
|
//from https://github.com/meetup/android-device-names
|
||||||
//Converted to java using:
|
//Converted to java using:
|
||||||
//cat android_models.properties | awk -F'=' '{sub(/ *$/, "", $1)} sub(/^ */, "", $2) { if ($2 != "") print "humanReadableNames.put(\""$1"\",\"" $2 "\");"}'
|
//cat android_models.properties | awk -F'=' '{sub(/ *$/, "", $1)} sub(/^ */, "", $2) { if ($2 != "") print "humanReadableNames.put(\""$1"\",\"" $2 "\");"}'
|
||||||
private static HashMap<String,String> humanReadableNames = new HashMap<String,String>();
|
private final static HashMap<String,String> humanReadableNames = new HashMap<String,String>();
|
||||||
static {
|
static {
|
||||||
humanReadableNames.put("5860E","Coolpad Quattro 4G");
|
humanReadableNames.put("5860E","Coolpad Quattro 4G");
|
||||||
humanReadableNames.put("ADR6300","HTC Droid Incredible");
|
humanReadableNames.put("ADR6300","HTC Droid Incredible");
|
||||||
@@ -22,6 +24,9 @@ public class HumanDeviceNames {
|
|||||||
humanReadableNames.put("C5155","Kyocera Rise");
|
humanReadableNames.put("C5155","Kyocera Rise");
|
||||||
humanReadableNames.put("C5170","Kyocera Hydro");
|
humanReadableNames.put("C5170","Kyocera Hydro");
|
||||||
humanReadableNames.put("C6603","Sony Xperia Z");
|
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("Desire_HD","HTC Desire HD");
|
||||||
humanReadableNames.put("DROID2_GLOBAL","Motorola Droid 2 Global");
|
humanReadableNames.put("DROID2_GLOBAL","Motorola Droid 2 Global");
|
||||||
humanReadableNames.put("DROID2","Motorola Droid 2");
|
humanReadableNames.put("DROID2","Motorola Droid 2");
|
||||||
@@ -38,34 +43,45 @@ public class HumanDeviceNames {
|
|||||||
humanReadableNames.put("Galaxy_Nexus","Samsung Galaxy Nexus");
|
humanReadableNames.put("Galaxy_Nexus","Samsung Galaxy Nexus");
|
||||||
humanReadableNames.put("google_sdk","Android Emulator");
|
humanReadableNames.put("google_sdk","Android Emulator");
|
||||||
humanReadableNames.put("GT-I8160","Samsung Galaxy Ace 2");
|
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-I8190","Samsung Galaxy S III Mini");
|
||||||
humanReadableNames.put("GT-I9000","Samsung Galaxy S");
|
humanReadableNames.put("GT-I9000","Samsung Galaxy S");
|
||||||
humanReadableNames.put("GT-I9001","Samsung Galaxy S Plus");
|
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-I9100M","Samsung Galaxy S II");
|
||||||
humanReadableNames.put("GT-I9100P","Samsung Galaxy S II");
|
humanReadableNames.put("GT-I9100P","Samsung Galaxy S II");
|
||||||
humanReadableNames.put("GT-I9100","Samsung Galaxy S II");
|
humanReadableNames.put("GT-I9100","Samsung Galaxy S II");
|
||||||
humanReadableNames.put("GT-I9100T","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-I9300","Samsung Galaxy S III");
|
||||||
humanReadableNames.put("GT-I9300T","Samsung Galaxy S III");
|
humanReadableNames.put("GT-I9300T","Samsung Galaxy S III");
|
||||||
humanReadableNames.put("GT-I9305","Samsung Galaxy S III");
|
humanReadableNames.put("GT-I9305","Samsung Galaxy S III");
|
||||||
humanReadableNames.put("GT-I9505","Samsung Galaxy S 4");
|
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-N7000","Samsung Galaxy Note");
|
humanReadableNames.put("GT-N7000","Samsung Galaxy Note");
|
||||||
humanReadableNames.put("GT-N7100","Samsung Galaxy Note II");
|
humanReadableNames.put("GT-N7100","Samsung Galaxy Note II");
|
||||||
humanReadableNames.put("GT-N7105","Samsung Galaxy Note II");
|
humanReadableNames.put("GT-N7105","Samsung Galaxy Note II");
|
||||||
humanReadableNames.put("GT-N8013","Samsung Galaxy Note 10.1");
|
humanReadableNames.put("GT-N8013","Samsung Galaxy Note 10.1");
|
||||||
humanReadableNames.put("GT-P3113","Samsung Galaxy Tab 2 7.0");
|
humanReadableNames.put("GT-P3113","Samsung Galaxy Tab 2 7.0");
|
||||||
humanReadableNames.put("GT-P5113","Samsnung Galaxy Tab 2 10.1");
|
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-P7510","Samsung Galaxy Tab 10.1");
|
||||||
humanReadableNames.put("GT-S5360","Samsung Galaxy Y");
|
humanReadableNames.put("GT-S5360","Samsung Galaxy Y");
|
||||||
humanReadableNames.put("GT-S5570","Samsung Galaxy Mini");
|
humanReadableNames.put("GT-S5570","Samsung Galaxy Mini");
|
||||||
humanReadableNames.put("GT-S5830i","Samsung Galaxy Ace");
|
humanReadableNames.put("GT-S5830i","Samsung Galaxy Ace");
|
||||||
humanReadableNames.put("GT-S5830","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("HTC6435LVW","HTC Droid DNA");
|
||||||
|
humanReadableNames.put("HTC6500LVW","HTC One");
|
||||||
humanReadableNames.put("HTC_Desire_HD_A9191","HTC Desire HD");
|
humanReadableNames.put("HTC_Desire_HD_A9191","HTC Desire HD");
|
||||||
humanReadableNames.put("HTCEVODesign4G","HTC Evo Design 4G");
|
humanReadableNames.put("HTCEVODesign4G","HTC Evo Design 4G");
|
||||||
humanReadableNames.put("HTCEVOV4G","HTC Evo V 4G");
|
humanReadableNames.put("HTCEVOV4G","HTC Evo V 4G");
|
||||||
humanReadableNames.put("HTCONE","HTC One");
|
humanReadableNames.put("HTCONE","HTC One");
|
||||||
humanReadableNames.put("HTC_PH39100","HTC Vivid 4G");
|
humanReadableNames.put("HTC_PH39100","HTC Vivid 4G");
|
||||||
|
humanReadableNames.put("HTC_PN071","HTC One");
|
||||||
humanReadableNames.put("HTC_Sensation_Z710e","HTC Sensation");
|
humanReadableNames.put("HTC_Sensation_Z710e","HTC Sensation");
|
||||||
humanReadableNames.put("HTC_VLE_U","HTC One S");
|
humanReadableNames.put("HTC_VLE_U","HTC One S");
|
||||||
humanReadableNames.put("KFJWA","Kindle Fire HD 8.9");
|
humanReadableNames.put("KFJWA","Kindle Fire HD 8.9");
|
||||||
@@ -73,9 +89,17 @@ public class HumanDeviceNames {
|
|||||||
humanReadableNames.put("KFOT","Kindle Fire");
|
humanReadableNames.put("KFOT","Kindle Fire");
|
||||||
humanReadableNames.put("KFTT","Kindle Fire HD 7");
|
humanReadableNames.put("KFTT","Kindle Fire HD 7");
|
||||||
humanReadableNames.put("LG-C800","LG myTouch Q");
|
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-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("LGL55C","LG LGL55C");
|
||||||
|
humanReadableNames.put("LG-LS840","LG Viper");
|
||||||
humanReadableNames.put("LG-LS970","LG Optimus G");
|
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-MS770","LG Motion 4G");
|
||||||
humanReadableNames.put("LG-MS910","LG Esteem");
|
humanReadableNames.put("LG-MS910","LG Esteem");
|
||||||
humanReadableNames.put("LG-P509","LG Optimus T");
|
humanReadableNames.put("LG-P509","LG Optimus T");
|
||||||
@@ -91,18 +115,20 @@ public class HumanDeviceNames {
|
|||||||
humanReadableNames.put("MOTWX435KT","Motorola Triumph");
|
humanReadableNames.put("MOTWX435KT","Motorola Triumph");
|
||||||
humanReadableNames.put("myTouch_4G_Slide","HTC myTouch 4G Slide");
|
humanReadableNames.put("myTouch_4G_Slide","HTC myTouch 4G Slide");
|
||||||
humanReadableNames.put("N860","ZTE Warp N860");
|
humanReadableNames.put("N860","ZTE Warp N860");
|
||||||
humanReadableNames.put("Nexus_10","Google Nexus 10");
|
humanReadableNames.put("Nexus_10","Nexus 10");
|
||||||
humanReadableNames.put("Nexus_4","Google Nexus 4");
|
humanReadableNames.put("Nexus_4","Nexus 4");
|
||||||
humanReadableNames.put("Nexus_7","Asus Nexus 7");
|
humanReadableNames.put("Nexus_5","Nexus 5");
|
||||||
humanReadableNames.put("Nexus_S_4G","Samsung Nexus S 4G");
|
humanReadableNames.put("Nexus_7","Nexus 7");
|
||||||
humanReadableNames.put("Nexus_S","Samsung Nexus S");
|
humanReadableNames.put("Nexus_S_4G","Nexus S 4G");
|
||||||
|
humanReadableNames.put("Nexus_S","Nexus S");
|
||||||
humanReadableNames.put("PantechP9070","Pantech Burst");
|
humanReadableNames.put("PantechP9070","Pantech Burst");
|
||||||
humanReadableNames.put("PC36100","HTC Evo 4G");
|
humanReadableNames.put("PC36100","HTC Evo 4G");
|
||||||
humanReadableNames.put("PG06100","HTC EVO Shift 4G");
|
humanReadableNames.put("PG06100","HTC EVO Shift 4G");
|
||||||
humanReadableNames.put("PG86100","HTC Evo 3D");
|
humanReadableNames.put("PG86100","HTC Evo 3D");
|
||||||
humanReadableNames.put("PH44100","HTC Evo Design 4G");
|
humanReadableNames.put("PH44100","HTC Evo Design 4G");
|
||||||
humanReadableNames.put("SAMSUNG-SGH-I317","Samsung Galaxy Note II");
|
humanReadableNames.put("SAMSUNG-SGH-I317","Samsung Galaxy Note II");
|
||||||
humanReadableNames.put("SAMSUNG-SGH-I337","Samsung Galaxy S 4");
|
humanReadableNames.put("SAMSUNG-SGH-I337","Samsung Galaxy S4");
|
||||||
|
humanReadableNames.put("SAMSUNG-SGH-I537","Samsung Galaxy S4 Active");
|
||||||
humanReadableNames.put("SAMSUNG-SGH-I717","Samsung Galaxy Note");
|
humanReadableNames.put("SAMSUNG-SGH-I717","Samsung Galaxy Note");
|
||||||
humanReadableNames.put("SAMSUNG-SGH-I727","Samsung Skyrocket");
|
humanReadableNames.put("SAMSUNG-SGH-I727","Samsung Skyrocket");
|
||||||
humanReadableNames.put("SAMSUNG-SGH-I747","Samsung Galaxy S III");
|
humanReadableNames.put("SAMSUNG-SGH-I747","Samsung Galaxy S III");
|
||||||
@@ -110,22 +136,27 @@ public class HumanDeviceNames {
|
|||||||
humanReadableNames.put("SAMSUNG-SGH-I897","Samsung Captivate");
|
humanReadableNames.put("SAMSUNG-SGH-I897","Samsung Captivate");
|
||||||
humanReadableNames.put("SAMSUNG-SGH-I927","Samsung Captivate Glide");
|
humanReadableNames.put("SAMSUNG-SGH-I927","Samsung Captivate Glide");
|
||||||
humanReadableNames.put("SAMSUNG-SGH-I997","Samsung Infuse 4G");
|
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-I200","Samsung Galaxy Stellar");
|
||||||
humanReadableNames.put("SCH-I405","Samsung Stratosphere");
|
humanReadableNames.put("SCH-I405","Samsung Stratosphere");
|
||||||
|
humanReadableNames.put("SCH-I415","Samsung Galaxy Stratosphere II");
|
||||||
humanReadableNames.put("SCH-I500","Samsung Fascinate");
|
humanReadableNames.put("SCH-I500","Samsung Fascinate");
|
||||||
humanReadableNames.put("SCH-I510","Samsung Droid Charge");
|
humanReadableNames.put("SCH-I510","Samsung Droid Charge");
|
||||||
humanReadableNames.put("SCH-I535","Samsung Galaxy S III");
|
humanReadableNames.put("SCH-I535","Samsung Galaxy S III");
|
||||||
humanReadableNames.put("SCH-I545","Samsung Galaxy S 4");
|
humanReadableNames.put("SCH-I545","Samsung Galaxy S4");
|
||||||
humanReadableNames.put("SCH-I605","Samsung Galaxy Note II");
|
humanReadableNames.put("SCH-I605","Samsung Galaxy Note II");
|
||||||
humanReadableNames.put("SCH-I800","Samsung Galaxy Tab 7.0");
|
humanReadableNames.put("SCH-I800","Samsung Galaxy Tab 7.0");
|
||||||
humanReadableNames.put("SCH-R530M","Samsung Galaxy S III");
|
humanReadableNames.put("SCH-R530M","Samsung Galaxy S III");
|
||||||
humanReadableNames.put("SCH-R530U","Samsung Galaxy S III");
|
humanReadableNames.put("SCH-R530U","Samsung Galaxy S III");
|
||||||
humanReadableNames.put("SCH-R720","Samsung Admire");
|
humanReadableNames.put("SCH-R720","Samsung Admire");
|
||||||
humanReadableNames.put("SCH-S720C","Samsung Proclaim");
|
humanReadableNames.put("SCH-S720C","Samsung Proclaim");
|
||||||
|
humanReadableNames.put("SCH-S738C","Samsung Galaxy Centura");
|
||||||
humanReadableNames.put("SGH-I317M","Samsung Galaxy Note II");
|
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-I727R","Samsung Galaxy S II");
|
||||||
humanReadableNames.put("SGH-I747M","Samsung Galaxy S III");
|
humanReadableNames.put("SGH-I747M","Samsung Galaxy S III");
|
||||||
humanReadableNames.put("SGH-M919","Samsung Galaxy S 4");
|
humanReadableNames.put("SGH-M919","Samsung Galaxy S4");
|
||||||
|
humanReadableNames.put("SGH-T599N","Samsung Galaxy Exhibit");
|
||||||
humanReadableNames.put("SGH-T679","Samsung Exhibit II");
|
humanReadableNames.put("SGH-T679","Samsung Exhibit II");
|
||||||
humanReadableNames.put("SGH-T769","Samsung Galaxy S Blaze");
|
humanReadableNames.put("SGH-T769","Samsung Galaxy S Blaze");
|
||||||
humanReadableNames.put("SGH-T889","Samsung Galaxy Note II");
|
humanReadableNames.put("SGH-T889","Samsung Galaxy Note II");
|
||||||
@@ -133,28 +164,48 @@ public class HumanDeviceNames {
|
|||||||
humanReadableNames.put("SGH-T959V","Samsung Galaxy S 4G");
|
humanReadableNames.put("SGH-T959V","Samsung Galaxy S 4G");
|
||||||
humanReadableNames.put("SGH-T989D","Samsung Galaxy S II");
|
humanReadableNames.put("SGH-T989D","Samsung Galaxy S II");
|
||||||
humanReadableNames.put("SGH-T989","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-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-D600","Samsung Conquer 4G");
|
||||||
humanReadableNames.put("SPH-D700","Samsung Epic 4G");
|
humanReadableNames.put("SPH-D700","Samsung Epic 4G");
|
||||||
humanReadableNames.put("SPH-D710BST","Samsung Galaxy S II");
|
humanReadableNames.put("SPH-D710BST","Samsung Galaxy S II");
|
||||||
humanReadableNames.put("SPH-D710","Samsung Epic");
|
humanReadableNames.put("SPH-D710","Samsung Epic");
|
||||||
humanReadableNames.put("SPH-D710VMUB","Samsung Galaxy S II");
|
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-L710","Samsung Galaxy S III");
|
||||||
humanReadableNames.put("SPH-L720","Samsung Galaxy S 4");
|
humanReadableNames.put("SPH-L720","Samsung Galaxy S4");
|
||||||
humanReadableNames.put("SPH-L900","Samsung Galaxy Note II");
|
humanReadableNames.put("SPH-L900","Samsung Galaxy Note II");
|
||||||
humanReadableNames.put("SPH-M820-BST","Samsung Galaxy Prevail");
|
humanReadableNames.put("SPH-M820-BST","Samsung Galaxy Prevail");
|
||||||
|
humanReadableNames.put("SPH-M830","Samsung Galaxy Rush");
|
||||||
humanReadableNames.put("SPH-M930BST","Samsung Transform Ultra");
|
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_Prime_TF201","Asus Eee Pad Transformer Prime");
|
||||||
humanReadableNames.put("Transformer_TF101","Asus Eee Pad Transformer");
|
humanReadableNames.put("Transformer_TF101","Asus Eee Pad Transformer");
|
||||||
humanReadableNames.put("VM670","LG Optimus V");
|
humanReadableNames.put("VM670","LG Optimus V");
|
||||||
humanReadableNames.put("VS840_4G","LG Lucid 4G");
|
humanReadableNames.put("VS840_4G","LG Lucid 4G");
|
||||||
humanReadableNames.put("VS910_4G","LG Revolution 4G");
|
humanReadableNames.put("VS910_4G","LG Revolution 4G");
|
||||||
humanReadableNames.put("VS920_4G","LG Spectrum 4G");
|
humanReadableNames.put("VS920_4G","LG Spectrum 4G");
|
||||||
|
humanReadableNames.put("VS980_4G","LG G2");
|
||||||
humanReadableNames.put("Xoom","Motorola Xoom");
|
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");
|
humanReadableNames.put("XT907","Motorola Droid Razr M");
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getDeviceName() {
|
public static String getDeviceName() {
|
||||||
|
|
||||||
String dictName = humanReadableNames.get(Build.MODEL.replace(' ','_'));
|
String dictName = humanReadableNames.get(Build.MODEL.replace(' ','_'));
|
||||||
if (dictName != null) return dictName;
|
if (dictName != null) return dictName;
|
||||||
@@ -167,4 +218,11 @@ public class HumanDeviceNames {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
56
src/main/java/org/kde/kdeconnect/Helpers/FilesHelper.java
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
101
src/main/java/org/kde/kdeconnect/Helpers/StorageHelper.java
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package org.kde.kdeconnect.Helpers;
|
||||||
|
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
//Code from http://stackoverflow.com/questions/9340332/how-can-i-get-the-list-of-mounted-external-storage-of-android-device/19982338#19982338
|
||||||
|
public class StorageHelper {
|
||||||
|
|
||||||
|
private static final String TAG = "StorageHelper";
|
||||||
|
|
||||||
|
public static class StorageInfo {
|
||||||
|
|
||||||
|
public final String path;
|
||||||
|
public final boolean readonly;
|
||||||
|
public final boolean removable;
|
||||||
|
public final int number;
|
||||||
|
|
||||||
|
StorageInfo(String path, boolean readonly, boolean removable, int number) {
|
||||||
|
this.path = path;
|
||||||
|
this.readonly = readonly;
|
||||||
|
this.removable = removable;
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<StorageInfo> getStorageList() {
|
||||||
|
|
||||||
|
List<StorageInfo> list = new ArrayList<StorageInfo>();
|
||||||
|
String def_path = Environment.getExternalStorageDirectory().getPath();
|
||||||
|
boolean def_path_removable = Environment.isExternalStorageRemovable();
|
||||||
|
String def_path_state = Environment.getExternalStorageState();
|
||||||
|
boolean def_path_available = def_path_state.equals(Environment.MEDIA_MOUNTED)
|
||||||
|
|| def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
|
||||||
|
boolean def_path_readonly = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
|
||||||
|
|
||||||
|
HashSet<String> paths = new HashSet<String>();
|
||||||
|
int cur_removable_number = 1;
|
||||||
|
|
||||||
|
if (def_path_available) {
|
||||||
|
paths.add(def_path);
|
||||||
|
list.add(0, new StorageInfo(def_path, def_path_readonly, def_path_removable, def_path_removable ? cur_removable_number++ : -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedReader buf_reader = null;
|
||||||
|
try {
|
||||||
|
buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
|
||||||
|
String line;
|
||||||
|
Log.d(TAG, "/proc/mounts");
|
||||||
|
while ((line = buf_reader.readLine()) != null) {
|
||||||
|
Log.d(TAG, line);
|
||||||
|
if (line.contains("vfat") || line.contains("/mnt")) {
|
||||||
|
StringTokenizer tokens = new StringTokenizer(line, " ");
|
||||||
|
String unused = tokens.nextToken(); //device
|
||||||
|
String mount_point = tokens.nextToken(); //mount point
|
||||||
|
if (paths.contains(mount_point)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unused = tokens.nextToken(); //file system
|
||||||
|
List<String> flags = Arrays.asList(tokens.nextToken().split(",")); //flags
|
||||||
|
boolean readonly = flags.contains("ro");
|
||||||
|
|
||||||
|
if (line.contains("/dev/block/vold")) {
|
||||||
|
if (!line.contains("/mnt/secure")
|
||||||
|
&& !line.contains("/mnt/asec")
|
||||||
|
&& !line.contains("/mnt/obb")
|
||||||
|
&& !line.contains("/dev/mapper")
|
||||||
|
&& !line.contains("tmpfs")) {
|
||||||
|
paths.add(mount_point);
|
||||||
|
list.add(new StorageInfo(mount_point, readonly, true, cur_removable_number++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (FileNotFoundException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (buf_reader != null) {
|
||||||
|
try {
|
||||||
|
buf_reader.close();
|
||||||
|
} catch (IOException ex) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -8,9 +8,12 @@ import android.util.Base64;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
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.nio.charset.Charset;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
@@ -20,29 +23,40 @@ import javax.crypto.Cipher;
|
|||||||
|
|
||||||
public class NetworkPackage {
|
public class NetworkPackage {
|
||||||
|
|
||||||
public final static int ProtocolVersion = 3;
|
public final static int ProtocolVersion = 5;
|
||||||
|
|
||||||
|
//TODO: Move these to their respective plugins
|
||||||
public final static String PACKAGE_TYPE_IDENTITY = "kdeconnect.identity";
|
public final static String PACKAGE_TYPE_IDENTITY = "kdeconnect.identity";
|
||||||
public final static String PACKAGE_TYPE_PAIR = "kdeconnect.pair";
|
public final static String PACKAGE_TYPE_PAIR = "kdeconnect.pair";
|
||||||
public final static String PACKAGE_TYPE_ENCRYPTED = "kdeconnect.encrypted";
|
public final static String PACKAGE_TYPE_ENCRYPTED = "kdeconnect.encrypted";
|
||||||
public final static String PACKAGE_TYPE_PING = "kdeconnect.ping";
|
public final static String PACKAGE_TYPE_PING = "kdeconnect.ping";
|
||||||
public final static String PACKAGE_TYPE_TELEPHONY = "kdeconnect.telephony";
|
public final static String PACKAGE_TYPE_TELEPHONY = "kdeconnect.telephony";
|
||||||
public final static String PACKAGE_TYPE_BATTERY = "kdeconnect.battery";
|
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_NOTIFICATION = "kdeconnect.notification";
|
||||||
public final static String PACKAGE_TYPE_CLIPBOARD = "kdeconnect.clipboard";
|
public final static String PACKAGE_TYPE_CLIPBOARD = "kdeconnect.clipboard";
|
||||||
public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris";
|
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 long mId;
|
||||||
private String mType;
|
private String mType;
|
||||||
private JSONObject mBody;
|
private JSONObject mBody;
|
||||||
|
private InputStream mPayload;
|
||||||
|
private JSONObject mPayloadTransferInfo;
|
||||||
|
private int mPayloadSize;
|
||||||
|
|
||||||
private NetworkPackage() {
|
private NetworkPackage() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkPackage(String type) {
|
public NetworkPackage(String type) {
|
||||||
mId = System.currentTimeMillis();
|
mId = System.currentTimeMillis();
|
||||||
mType = type;
|
mType = type;
|
||||||
mBody = new JSONObject();
|
mBody = new JSONObject();
|
||||||
|
mPayload = null;
|
||||||
|
mPayloadSize = 0;
|
||||||
|
mPayloadTransferInfo = new JSONObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getType() {
|
public String getType() {
|
||||||
@@ -62,6 +76,9 @@ public class NetworkPackage {
|
|||||||
public double getDouble(String key) { return mBody.optDouble(key,Double.NaN); }
|
public double getDouble(String key) { return mBody.optDouble(key,Double.NaN); }
|
||||||
public double getDouble(String key, double defaultValue) { return mBody.optDouble(key,defaultValue); }
|
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 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) {
|
public ArrayList<String> getStringList(String key) {
|
||||||
JSONArray jsonArray = mBody.optJSONArray(key);
|
JSONArray jsonArray = mBody.optJSONArray(key);
|
||||||
ArrayList<String> list = new ArrayList<String>();
|
ArrayList<String> list = new ArrayList<String>();
|
||||||
@@ -91,40 +108,64 @@ public class NetworkPackage {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean has(String key) { return mBody.has(key); }
|
public boolean has(String key) { return mBody.has(key); }
|
||||||
|
|
||||||
|
public boolean isEncrypted() { return mType.equals(PACKAGE_TYPE_ENCRYPTED); }
|
||||||
|
|
||||||
public String serialize() {
|
public String serialize() {
|
||||||
JSONObject jo = new JSONObject();
|
JSONObject jo = new JSONObject();
|
||||||
try {
|
try {
|
||||||
jo.put("id",mId);
|
jo.put("id", mId);
|
||||||
jo.put("type",mType);
|
jo.put("type", mType);
|
||||||
jo.put("body",mBody);
|
jo.put("body", mBody);
|
||||||
|
if (hasPayload()) {
|
||||||
|
jo.put("payloadSize", mPayloadSize);
|
||||||
|
jo.put("payloadTransferInfo", mPayloadTransferInfo);
|
||||||
|
}
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("NetworkPackage", "Serialization exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
//QJSon does not escape slashes, but Java JSONObject does. Converting to QJson format.
|
//QJSon does not escape slashes, but Java JSONObject does. Converting to QJson format.
|
||||||
String json = jo.toString().replace("\\/","/")+"\n";
|
String json = jo.toString().replace("\\/","/")+"\n";
|
||||||
//Log.e("NetworkPackage.serialize",json);
|
|
||||||
|
if (!isEncrypted()) {
|
||||||
|
//Log.e("NetworkPackage.serialize", json);
|
||||||
|
}
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public NetworkPackage unserialize(String s) {
|
static public NetworkPackage unserialize(String s) {
|
||||||
//Log.e("NetworkPackage.unserialize", s);
|
|
||||||
NetworkPackage np = new NetworkPackage();
|
NetworkPackage np = new NetworkPackage();
|
||||||
try {
|
try {
|
||||||
JSONObject jo = new JSONObject(s);
|
JSONObject jo = new JSONObject(s);
|
||||||
np.mId = jo.getLong("id");
|
np.mId = jo.getLong("id");
|
||||||
np.mType = jo.getString("type");
|
np.mType = jo.getString("type");
|
||||||
np.mBody = jo.getJSONObject("body");
|
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) {
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("NetworkPackage", "Unserialization exception unserializing "+s);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!np.isEncrypted()) {
|
||||||
|
//Log.e("NetworkPackage.unserialize", s);
|
||||||
|
}
|
||||||
|
|
||||||
return np;
|
return np;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NetworkPackage encrypt(PublicKey publicKey) throws Exception {
|
||||||
|
|
||||||
public void encrypt(PublicKey publicKey) throws Exception {
|
|
||||||
|
|
||||||
String serialized = serialize();
|
String serialized = serialize();
|
||||||
|
|
||||||
@@ -146,17 +187,11 @@ public class NetworkPackage {
|
|||||||
chunks.put(Base64.encodeToString(encryptedChunk, Base64.NO_WRAP));
|
chunks.put(Base64.encodeToString(encryptedChunk, Base64.NO_WRAP));
|
||||||
}
|
}
|
||||||
|
|
||||||
mId = System.currentTimeMillis();
|
//Log.i("NetworkPackage", "Encrypted " + chunks.length()+" chunks");
|
||||||
mType = NetworkPackage.PACKAGE_TYPE_ENCRYPTED;
|
|
||||||
mBody = new JSONObject();
|
|
||||||
try {
|
|
||||||
mBody.put("data", chunks);
|
|
||||||
}catch(Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
Log.e("NetworkPackage","Exception");
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i("NetworkPackage", "Encrypted " + chunks.length()+" chunks");
|
NetworkPackage encrypted = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_ENCRYPTED);
|
||||||
|
encrypted.set("data", chunks);
|
||||||
|
return encrypted;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +212,6 @@ public class NetworkPackage {
|
|||||||
return unserialize(decryptedJson);
|
return unserialize(decryptedJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static public NetworkPackage createIdentityPackage(Context context) {
|
static public NetworkPackage createIdentityPackage(Context context) {
|
||||||
|
|
||||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY);
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY);
|
||||||
@@ -185,15 +219,22 @@ public class NetworkPackage {
|
|||||||
String deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
|
String deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||||
try {
|
try {
|
||||||
np.mBody.put("deviceId", deviceId);
|
np.mBody.put("deviceId", deviceId);
|
||||||
np.mBody.put("deviceName", HumanDeviceNames.getDeviceName());
|
np.mBody.put("deviceName",
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(context).getString(
|
||||||
|
MainSettingsActivity.KEY_DEVICE_NAME_PREFERENCE,
|
||||||
|
DeviceHelper.getDeviceName()));
|
||||||
np.mBody.put("protocolVersion", NetworkPackage.ProtocolVersion);
|
np.mBody.put("protocolVersion", NetworkPackage.ProtocolVersion);
|
||||||
} catch (JSONException e) {
|
np.mBody.put("deviceType", DeviceHelper.isTablet()? "tablet" : "phone");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("NetworkPacakge","Exception on createIdentityPackage");
|
||||||
}
|
}
|
||||||
|
|
||||||
return np;
|
return np;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static public NetworkPackage createPublicKeyPackage(Context context) {
|
static public NetworkPackage createPublicKeyPackage(Context context) {
|
||||||
|
|
||||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
|
||||||
@@ -208,4 +249,40 @@ 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,7 +8,6 @@ import android.content.Intent;
|
|||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.BatteryManager;
|
import android.os.BatteryManager;
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
@@ -17,13 +16,11 @@ import org.kde.kdeconnect_tp.R;
|
|||||||
|
|
||||||
public class BatteryPlugin extends Plugin {
|
public class BatteryPlugin extends Plugin {
|
||||||
|
|
||||||
private NetworkPackage lastPackage = null;
|
// 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 IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
|
private NetworkPackage lastInfo = null;
|
||||||
|
|
||||||
/*static {
|
|
||||||
PluginFactory.registerPlugin(BatteryPlugin.class);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPluginName() {
|
public String getPluginName() {
|
||||||
@@ -45,41 +42,46 @@ public class BatteryPlugin extends Plugin {
|
|||||||
return context.getResources().getDrawable(R.drawable.icon);
|
return context.getResources().getDrawable(R.drawable.icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledByDefault() {
|
public boolean isEnabledByDefault() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||||
private BroadcastReceiver receiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent batteryIntent) {
|
||||||
|
|
||||||
Log.i("BatteryPlugin", "Battery event");
|
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;
|
||||||
|
|
||||||
boolean isCharging = (0 != intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0));
|
if (lastInfo != null
|
||||||
|
&& isCharging != lastInfo.getBoolean("isCharging")
|
||||||
int currentCharge = 100;
|
&& currentCharge != lastInfo.getInt("currentCharge")
|
||||||
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
|
&& thresholdEvent != lastInfo.getInt("thresholdEvent")
|
||||||
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);
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_BATTERY);
|
||||||
np.set("isCharging", isCharging);
|
|
||||||
np.set("currentCharge", currentCharge);
|
np.set("currentCharge", currentCharge);
|
||||||
|
np.set("isCharging", isCharging);
|
||||||
|
np.set("thresholdEvent", thresholdEvent);
|
||||||
device.sendPackage(np);
|
device.sendPackage(np);
|
||||||
lastPackage = np;
|
lastInfo = np;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -87,12 +89,14 @@ public class BatteryPlugin extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
context.registerReceiver(receiver, filter);
|
context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||||
|
context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_BATTERY_LOW));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
|
//It's okay to call this only once, even though we registered it for two filters
|
||||||
context.unregisterReceiver(receiver);
|
context.unregisterReceiver(receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,8 +105,8 @@ public class BatteryPlugin extends Plugin {
|
|||||||
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_BATTERY)) return false;
|
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_BATTERY)) return false;
|
||||||
|
|
||||||
if (np.getBoolean("request")) {
|
if (np.getBoolean("request")) {
|
||||||
if (lastPackage != null) {
|
if (lastInfo != null) {
|
||||||
device.sendPackage(lastPackage);
|
device.sendPackage(lastInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +114,7 @@ public class BatteryPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AlertDialog getErrorDialog(Context baseContext) {
|
public AlertDialog getErrorDialog(Activity deviceActivity) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@@ -10,12 +10,18 @@ import org.kde.kdeconnect.NetworkPackage;
|
|||||||
public class ClipboardListener {
|
public class ClipboardListener {
|
||||||
|
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
private String currentContent;
|
private String currentContent;
|
||||||
|
|
||||||
private ClipboardManager cm = null;
|
private ClipboardManager cm = null;
|
||||||
ClipboardManager.OnPrimaryClipChangedListener listener;
|
private ClipboardManager.OnPrimaryClipChangedListener listener;
|
||||||
|
|
||||||
|
ClipboardListener(final Context ctx, final Device device) {
|
||||||
|
context = ctx;
|
||||||
|
if(android.os.Build.VERSION.SDK_INT < 11) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ClipboardListener(final Context context, final Device device) {
|
|
||||||
cm = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
|
cm = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
listener = new ClipboardManager.OnPrimaryClipChangedListener() {
|
listener = new ClipboardManager.OnPrimaryClipChangedListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -41,12 +47,24 @@ public class ClipboardListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
|
if(android.os.Build.VERSION.SDK_INT < 11) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
cm.removePrimaryClipChangedListener(listener);
|
cm.removePrimaryClipChangedListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public void setText(String text) {
|
public void setText(String text) {
|
||||||
currentContent = text;
|
currentContent = text;
|
||||||
cm.setText(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -3,9 +3,7 @@ package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
@@ -14,10 +12,6 @@ import org.kde.kdeconnect_tp.R;
|
|||||||
|
|
||||||
public class ClipboardPlugin extends Plugin {
|
public class ClipboardPlugin extends Plugin {
|
||||||
|
|
||||||
/*static {
|
|
||||||
PluginFactory.registerPlugin(ClipboardPlugin.class);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPluginName() {
|
public String getPluginName() {
|
||||||
return "plugin_clipboard";
|
return "plugin_clipboard";
|
||||||
@@ -37,33 +31,28 @@ public class ClipboardPlugin extends Plugin {
|
|||||||
return context.getResources().getDrawable(R.drawable.icon);
|
return context.getResources().getDrawable(R.drawable.icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledByDefault() {
|
public boolean isEnabledByDefault() {
|
||||||
return (Build.VERSION.SDK_INT >= 11);
|
//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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClipboardListener listener;
|
private ClipboardListener listener;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < 11) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
listener = new ClipboardListener(context, device);
|
listener = new ClipboardListener(context, device);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < 11) return;
|
|
||||||
|
|
||||||
listener.stop();
|
listener.stop();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -76,22 +65,10 @@ public class ClipboardPlugin extends Plugin {
|
|||||||
String content = np.getString("content");
|
String content = np.getString("content");
|
||||||
listener.setText(content);
|
listener.setText(content);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AlertDialog getErrorDialog(Context baseContext) {
|
public AlertDialog getErrorDialog(Activity deviceActivity) { return null; }
|
||||||
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
|
@Override
|
||||||
public Button getInterfaceButton(Activity activity) {
|
public Button getInterfaceButton(Activity activity) {
|
@@ -0,0 +1,103 @@
|
|||||||
|
package org.kde.kdeconnect.Plugins.MousePadPlugin;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputConnection;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.BackgroundService;
|
||||||
|
import org.kde.kdeconnect.Device;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class KeyListenerView extends View {
|
||||||
|
|
||||||
|
private String deviceId;
|
||||||
|
|
||||||
|
private static HashMap<Integer, Integer> SpecialKeysMap = new HashMap<Integer, Integer>();
|
||||||
|
static {
|
||||||
|
int i = 0;
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_DEL, ++i); // 1
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_TAB, ++i); // 2
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_ENTER, 12); ++i; // 3 is not used, return is 12 instead
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_DPAD_LEFT, ++i); // 4
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_DPAD_UP, ++i); // 5
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_DPAD_RIGHT, ++i); // 6
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_DPAD_DOWN, ++i); // 7
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_PAGE_UP, ++i); // 8
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_PAGE_DOWN, ++i); // 9
|
||||||
|
if (Build.VERSION.SDK_INT >= 11) {
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_MOVE_HOME, ++i); // 10
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_MOVE_END, ++i); // 11
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_NUMPAD_ENTER, ++i); // 12
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_FORWARD_DEL, ++i); // 13
|
||||||
|
SpecialKeysMap.put(KeyEvent.KEYCODE_ESCAPE, ++i); // 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceId(String id) {
|
||||||
|
deviceId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyListenerView(Context context, AttributeSet set) {
|
||||||
|
super(context, set);
|
||||||
|
|
||||||
|
setFocusable(true);
|
||||||
|
setFocusableInTouchMode(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||||
|
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCheckIsTextEditor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||||
|
|
||||||
|
char utfChar = (char)event.getUnicodeChar();
|
||||||
|
if (utfChar == 9 || utfChar == 10) utfChar = 0; //Workaround to send enter and tab as special keys instead of characters
|
||||||
|
|
||||||
|
if (utfChar != 0) {
|
||||||
|
final String utfString = new String(new char[]{utfChar});
|
||||||
|
BackgroundService.RunCommand(getContext(), 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.sendKey(utfString);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (!SpecialKeysMap.containsKey(keyCode)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int specialKey = SpecialKeysMap.get(keyCode);
|
||||||
|
BackgroundService.RunCommand(getContext(), 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.sendSpecialKey(specialKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,254 @@
|
|||||||
|
package org.kde.kdeconnect.Plugins.MousePadPlugin;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.ActionBarActivity;
|
||||||
|
import android.view.GestureDetector;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
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 ActionBarActivity implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, MousePadGestureDetector.OnGestureListener {
|
||||||
|
|
||||||
|
String deviceId;
|
||||||
|
|
||||||
|
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 GestureDetector mDetector;
|
||||||
|
private MousePadGestureDetector mMousePadGestureDetector;
|
||||||
|
|
||||||
|
KeyListenerView keyListenerView;
|
||||||
|
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
keyListenerView = (KeyListenerView)findViewById(R.id.keyListener);
|
||||||
|
keyListenerView.setDeviceId(deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
case R.id.menu_show_keyboard:
|
||||||
|
showKeyboard();
|
||||||
|
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();
|
||||||
|
|
||||||
|
if (isScrolling) {
|
||||||
|
if (actionType == MotionEvent.ACTION_UP) {
|
||||||
|
isScrolling = false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (actionType) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
mPrevX = event.getX();
|
||||||
|
mPrevY = event.getY();
|
||||||
|
break;
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
mCurrentX = event.getX();
|
||||||
|
mCurrentY = event.getY();
|
||||||
|
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.sendMouseDelta(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) {
|
||||||
|
//From GestureDetector, left empty
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
//From GestureDetector, left empty
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showKeyboard() {
|
||||||
|
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.toggleSoftInputFromWindow(keyListenerView.getWindowToken(), 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@@ -0,0 +1,59 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,131 @@
|
|||||||
|
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 hasSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPackageReceived(NetworkPackage np) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AlertDialog getErrorDialog(Activity deviceActivity) { return null; }
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendMouseDelta(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendKey(String utfChar) {
|
||||||
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
|
||||||
|
np.set("key", utfChar);
|
||||||
|
device.sendPackage(np);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendSpecialKey(int specialKey) {
|
||||||
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
|
||||||
|
np.set("specialKey", specialKey);
|
||||||
|
device.sendPackage(np);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,10 +1,13 @@
|
|||||||
package org.kde.kdeconnect.Plugins.MprisPlugin;
|
package org.kde.kdeconnect.Plugins.MprisPlugin;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
@@ -13,10 +16,10 @@ import android.widget.SeekBar;
|
|||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.Backends.BaseLink;
|
||||||
import org.kde.kdeconnect.BackgroundService;
|
import org.kde.kdeconnect.BackgroundService;
|
||||||
import org.kde.kdeconnect.ComputerLinks.BaseComputerLink;
|
|
||||||
import org.kde.kdeconnect.Device;
|
import org.kde.kdeconnect.Device;
|
||||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
import org.kde.kdeconnect_tp.R;
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
@@ -24,9 +27,11 @@ import java.util.ArrayList;
|
|||||||
|
|
||||||
public class MprisActivity extends Activity {
|
public class MprisActivity extends Activity {
|
||||||
|
|
||||||
//TODO: Add a loading spinner at the begginning (to distinguish the loading state from a no-players state).
|
//TODO: Add a loading spinner at the beginning (to distinguish the loading state from a no-players state).
|
||||||
//TODO 2: Add a message when no players are detected after loading completes
|
//TODO 2: Add a message when no players are detected after loading completes
|
||||||
|
|
||||||
|
private String deviceId;
|
||||||
|
|
||||||
protected void connectToPlugin() {
|
protected void connectToPlugin() {
|
||||||
|
|
||||||
final String deviceId = getIntent().getStringExtra("deviceId");
|
final String deviceId = getIntent().getStringExtra("deviceId");
|
||||||
@@ -67,8 +72,6 @@ public class MprisActivity extends Activity {
|
|||||||
});
|
});
|
||||||
|
|
||||||
mpris.setPlayerListUpdatedHandler(new Handler() {
|
mpris.setPlayerListUpdatedHandler(new Handler() {
|
||||||
boolean firstLoad = true;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
final ArrayList<String> playerList = mpris.getPlayerList();
|
final ArrayList<String> playerList = mpris.getPlayerList();
|
||||||
@@ -93,24 +96,29 @@ public class MprisActivity extends Activity {
|
|||||||
//Spotify doesn't support changing the volume yet...
|
//Spotify doesn't support changing the volume yet...
|
||||||
if (player.equals("Spotify")) {
|
if (player.equals("Spotify")) {
|
||||||
findViewById(R.id.volume_layout).setVisibility(View.INVISIBLE);
|
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 {
|
} else {
|
||||||
findViewById(R.id.volume_layout).setVisibility(View.VISIBLE);
|
findViewById(R.id.volume_layout).setVisibility(View.VISIBLE);
|
||||||
|
findViewById(R.id.rew_button).setVisibility(View.VISIBLE);
|
||||||
|
findViewById(R.id.ff_button).setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNothingSelected(AdapterView<?> arg0) {
|
public void onNothingSelected(AdapterView<?> arg0) {
|
||||||
|
mpris.setPlayer(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// restore the selected player
|
||||||
|
int position = adapter.getPosition(mpris.getPlayer());
|
||||||
|
|
||||||
|
if (position >= 0) {
|
||||||
|
spinner.setSelection(position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (firstLoad) {
|
|
||||||
firstLoad = false;
|
|
||||||
if (playerList.size() > 0) {
|
|
||||||
mpris.setPlayer(playerList.get(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -119,14 +127,14 @@ public class MprisActivity extends Activity {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseLinkProvider.ConnectionReceiver connectionReceiver = new BaseLinkProvider.ConnectionReceiver() {
|
private final BaseLinkProvider.ConnectionReceiver connectionReceiver = new BaseLinkProvider.ConnectionReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionReceived(NetworkPackage identityPackage, BaseComputerLink link) {
|
public void onConnectionReceived(NetworkPackage identityPackage, BaseLink link) {
|
||||||
connectToPlugin();
|
connectToPlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionLost(BaseComputerLink link) {
|
public void onConnectionLost(BaseLink link) {
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -142,12 +150,78 @@ 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
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.mpris_control);
|
setContentView(R.layout.mpris_control);
|
||||||
|
|
||||||
final String deviceId = getIntent().getStringExtra("deviceId");
|
deviceId = getIntent().getStringExtra("deviceId");
|
||||||
|
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
String interval_time_str = prefs.getString(getString(R.string.mpris_time_key),
|
||||||
|
getString(R.string.mpris_time_default));
|
||||||
|
final int interval_time = Integer.parseInt(interval_time_str);
|
||||||
|
|
||||||
BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() {
|
BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() {
|
||||||
@Override
|
@Override
|
||||||
@@ -187,6 +261,36 @@ 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(interval_time * -1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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(interval_time);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
findViewById(R.id.next_button).setOnClickListener(new View.OnClickListener() {
|
findViewById(R.id.next_button).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
@@ -204,10 +308,12 @@ public class MprisActivity extends Activity {
|
|||||||
|
|
||||||
((SeekBar)findViewById(R.id.volume_seek)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
((SeekBar)findViewById(R.id.volume_seek)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onProgressChanged(SeekBar seekBar, int i, boolean b) { }
|
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStartTrackingTouch(SeekBar seekBar) { }
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStopTrackingTouch(final SeekBar seekBar) {
|
public void onStopTrackingTouch(final SeekBar seekBar) {
|
||||||
@@ -219,12 +325,14 @@ public class MprisActivity extends Activity {
|
|||||||
if (mpris == null) return;
|
if (mpris == null) return;
|
||||||
mpris.setVolume(seekBar.getProgress());
|
mpris.setVolume(seekBar.getProgress());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -30,10 +30,6 @@ public class MprisPlugin extends Plugin {
|
|||||||
private String player = "";
|
private String player = "";
|
||||||
private boolean playing = false;
|
private boolean playing = false;
|
||||||
|
|
||||||
/*static {
|
|
||||||
PluginFactory.registerPlugin(MprisPlugin.class);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPluginName() {
|
public String getPluginName() {
|
||||||
return "plugin_mpris";
|
return "plugin_mpris";
|
||||||
@@ -54,6 +50,11 @@ public class MprisPlugin extends Plugin {
|
|||||||
return context.getResources().getDrawable(R.drawable.icon);
|
return context.getResources().getDrawable(R.drawable.icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasSettings() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledByDefault() {
|
public boolean isEnabledByDefault() {
|
||||||
return true;
|
return true;
|
||||||
@@ -84,6 +85,13 @@ public class MprisPlugin extends Plugin {
|
|||||||
device.sendPackage(np);
|
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
|
@Override
|
||||||
public boolean onPackageReceived(NetworkPackage np) {
|
public boolean onPackageReceived(NetworkPackage np) {
|
||||||
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_MPRIS)) return false;
|
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_MPRIS)) return false;
|
||||||
@@ -172,6 +180,10 @@ public class MprisPlugin extends Plugin {
|
|||||||
requestPlayerStatus();
|
requestPlayerStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
public int getVolume() {
|
public int getVolume() {
|
||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
@@ -196,7 +208,7 @@ public class MprisPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AlertDialog getErrorDialog(Context baseContext) {
|
public AlertDialog getErrorDialog(Activity deviceActivity) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@@ -5,7 +5,6 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.service.notification.NotificationListenerService;
|
import android.service.notification.NotificationListenerService;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ public class NotificationReceiver extends NotificationListenerService {
|
|||||||
void onNotificationRemoved(StatusBarNotification statusBarNotification);
|
void onNotificationRemoved(StatusBarNotification statusBarNotification);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<NotificationListener> listeners = new ArrayList<NotificationListener>();
|
private final ArrayList<NotificationListener> listeners = new ArrayList<NotificationListener>();
|
||||||
|
|
||||||
public void addListener(NotificationListener listener) {
|
public void addListener(NotificationListener listener) {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
@@ -27,7 +26,7 @@ public class NotificationReceiver extends NotificationListenerService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNotificationPosted(StatusBarNotification statusBarNotification) {
|
public void onNotificationPosted(StatusBarNotification statusBarNotification) {
|
||||||
Log.i("NotificationReceiver.onNotificationPosted","listeners: " + listeners.size());
|
//Log.e("NotificationReceiver.onNotificationPosted","listeners: " + listeners.size());
|
||||||
for(NotificationListener listener : listeners) {
|
for(NotificationListener listener : listeners) {
|
||||||
listener.onNotificationPosted(statusBarNotification);
|
listener.onNotificationPosted(statusBarNotification);
|
||||||
}
|
}
|
||||||
@@ -49,7 +48,7 @@ public class NotificationReceiver extends NotificationListenerService {
|
|||||||
//This will be called for each intent launch, even if the service is already started and is reused
|
//This will be called for each intent launch, even if the service is already started and is reused
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
Log.i("NotificationReceiver", "onStartCommand");
|
//Log.e("NotificationReceiver", "onStartCommand");
|
||||||
for (InstanceCallback c : callbacks) {
|
for (InstanceCallback c : callbacks) {
|
||||||
c.onServiceStart(this);
|
c.onServiceStart(this);
|
||||||
}
|
}
|
||||||
@@ -61,7 +60,7 @@ public class NotificationReceiver extends NotificationListenerService {
|
|||||||
void onServiceStart(NotificationReceiver service);
|
void onServiceStart(NotificationReceiver service);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
|
private final static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
|
||||||
|
|
||||||
public static void Start(Context c) {
|
public static void Start(Context c) {
|
||||||
RunCommand(c, null);
|
RunCommand(c, null);
|
@@ -6,31 +6,22 @@ import android.app.Notification;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.util.Base64;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
|
||||||
import org.kde.kdeconnect.Helpers.AppsHelper;
|
import org.kde.kdeconnect.Helpers.AppsHelper;
|
||||||
import org.kde.kdeconnect.Helpers.ImagesHelper;
|
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
import org.kde.kdeconnect.Plugins.Plugin;
|
import org.kde.kdeconnect.Plugins.Plugin;
|
||||||
|
import org.kde.kdeconnect.UserInterface.DeviceActivity;
|
||||||
import org.kde.kdeconnect_tp.R;
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
|
|
||||||
public class NotificationsPlugin extends Plugin implements NotificationReceiver.NotificationListener {
|
public class NotificationsPlugin extends Plugin implements NotificationReceiver.NotificationListener {
|
||||||
|
|
||||||
/*static {
|
|
||||||
PluginFactory.registerPlugin(NotificationsPlugin.class);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPluginName() {
|
public String getPluginName() {
|
||||||
return "plugin_notifications";
|
return "plugin_notifications";
|
||||||
@@ -51,6 +42,11 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
return context.getResources().getDrawable(R.drawable.icon);
|
return context.getResources().getDrawable(R.drawable.icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledByDefault() {
|
public boolean isEnabledByDefault() {
|
||||||
return true;
|
return true;
|
||||||
@@ -116,7 +112,9 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < 18) return false;
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//Check for permissions
|
//Check for permissions
|
||||||
String notificationListenerList = Settings.Secure.getString(context.getContentResolver(), "enabled_notification_listeners");
|
String notificationListenerList = Settings.Secure.getString(context.getContentResolver(), "enabled_notification_listeners");
|
||||||
@@ -126,12 +124,10 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
public void onServiceStart(NotificationReceiver service) {
|
public void onServiceStart(NotificationReceiver service) {
|
||||||
try {
|
try {
|
||||||
service.addListener(NotificationsPlugin.this);
|
service.addListener(NotificationsPlugin.this);
|
||||||
/*
|
|
||||||
StatusBarNotification[] notifications = service.getActiveNotifications();
|
StatusBarNotification[] notifications = service.getActiveNotifications();
|
||||||
for (StatusBarNotification notification : notifications) {
|
for (StatusBarNotification notification : notifications) {
|
||||||
onNotificationPosted(notification);
|
sendNotification(notification, true);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Log.e("NotificationsPlugin","Exception");
|
Log.e("NotificationsPlugin","Exception");
|
||||||
@@ -148,6 +144,11 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
|
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onServiceStart(NotificationReceiver service) {
|
public void onServiceStart(NotificationReceiver service) {
|
||||||
@@ -172,12 +173,19 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNotificationPosted(StatusBarNotification statusBarNotification) {
|
public void onNotificationPosted(StatusBarNotification statusBarNotification) {
|
||||||
onNotificationPosted(statusBarNotification, false);
|
sendNotification(statusBarNotification, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onNotificationPosted(StatusBarNotification statusBarNotification, boolean requestAnswer) {
|
public void sendNotification(StatusBarNotification statusBarNotification, boolean requestAnswer) {
|
||||||
|
|
||||||
Notification notification = statusBarNotification.getNotification();
|
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);
|
NotificationId id = NotificationId.fromNotification(statusBarNotification);
|
||||||
|
|
||||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_NOTIFICATION);
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_NOTIFICATION);
|
||||||
@@ -185,25 +193,26 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
String packageName = statusBarNotification.getPackageName();
|
String packageName = statusBarNotification.getPackageName();
|
||||||
String appName = AppsHelper.appNameLookup(context, packageName);
|
String appName = AppsHelper.appNameLookup(context, packageName);
|
||||||
|
|
||||||
|
//TODO: Add support for displaying app icons to desktop plasmoid and uncomment this piece of code
|
||||||
|
/*
|
||||||
try {
|
try {
|
||||||
|
//TODO: Scale down app icon if too big and compress as JPG
|
||||||
Drawable drawableAppIcon = AppsHelper.appIconLookup(context, packageName);
|
Drawable drawableAppIcon = AppsHelper.appIconLookup(context, packageName);
|
||||||
Bitmap appIcon = ImagesHelper.drawableToBitmap(drawableAppIcon);
|
Bitmap appIcon = ImagesHelper.drawableToBitmap(drawableAppIcon);
|
||||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||||
appIcon.compress(Bitmap.CompressFormat.PNG, 90, outStream);
|
appIcon.compress(Bitmap.CompressFormat.PNG, 90, outStream);
|
||||||
byte[] bitmapData = outStream.toByteArray();
|
byte[] bitmapData = outStream.toByteArray();
|
||||||
byte[] serializedBitmapData = Base64.encode(bitmapData, Base64.NO_WRAP);
|
np.setPayload(bitmapData);
|
||||||
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) {
|
} catch(Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Log.e("NotificationsPlugin","Error retrieving icon");
|
Log.e("NotificationsPlugin","Error retrieving icon");
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
np.set("id", id.serialize());
|
np.set("id", id.serialize());
|
||||||
np.set("appName", appName == null? packageName : appName);
|
np.set("appName", appName == null? packageName : appName);
|
||||||
np.set("isClearable", statusBarNotification.isClearable());
|
np.set("isClearable", statusBarNotification.isClearable());
|
||||||
np.set("ticker", notification.tickerText.toString());
|
np.set("ticker", getTickerText(notification));
|
||||||
np.set("time", Long.toString(statusBarNotification.getPostTime()));
|
np.set("time", Long.toString(statusBarNotification.getPostTime()));
|
||||||
if (requestAnswer) np.set("requestAnswer", true);
|
if (requestAnswer) np.set("requestAnswer", true);
|
||||||
|
|
||||||
@@ -211,7 +220,43 @@ 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
|
@Override
|
||||||
@@ -224,7 +269,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
private void sendCurrentNotifications(NotificationReceiver service) {
|
private void sendCurrentNotifications(NotificationReceiver service) {
|
||||||
StatusBarNotification[] notifications = service.getActiveNotifications();
|
StatusBarNotification[] notifications = service.getActiveNotifications();
|
||||||
for (StatusBarNotification notification : notifications) {
|
for (StatusBarNotification notification : notifications) {
|
||||||
onNotificationPosted(notification, true);
|
sendNotification(notification, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,10 +320,10 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AlertDialog getErrorDialog(final Context baseContext) {
|
public AlertDialog getErrorDialog(final Activity deviceActivity) {
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < 18) {
|
if (Build.VERSION.SDK_INT < 18) {
|
||||||
return new AlertDialog.Builder(baseContext)
|
return new AlertDialog.Builder(deviceActivity)
|
||||||
.setTitle(R.string.pref_plugin_notifications)
|
.setTitle(R.string.pref_plugin_notifications)
|
||||||
.setMessage(R.string.plugin_not_available)
|
.setMessage(R.string.plugin_not_available)
|
||||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
@@ -289,14 +334,14 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
})
|
})
|
||||||
.create();
|
.create();
|
||||||
} else {
|
} else {
|
||||||
return new AlertDialog.Builder(baseContext)
|
return new AlertDialog.Builder(deviceActivity)
|
||||||
.setTitle(R.string.pref_plugin_notifications)
|
.setTitle(R.string.pref_plugin_notifications)
|
||||||
.setMessage(R.string.no_permissions)
|
.setMessage(R.string.no_permissions)
|
||||||
.setPositiveButton(R.string.open_settings, new DialogInterface.OnClickListener() {
|
.setPositiveButton(R.string.open_settings, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
|
Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
|
||||||
baseContext.startActivity(intent);
|
deviceActivity.startActivityForResult(intent, DeviceActivity.RESULT_NEEDS_RELOAD);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setNegativeButton(R.string.cancel,new DialogInterface.OnClickListener() {
|
.setNegativeButton(R.string.cancel,new DialogInterface.OnClickListener() {
|
@@ -4,23 +4,23 @@ import android.app.Activity;
|
|||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
import android.support.v4.app.TaskStackBuilder;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
import org.kde.kdeconnect.Plugins.Plugin;
|
import org.kde.kdeconnect.Plugins.Plugin;
|
||||||
|
import org.kde.kdeconnect.UserInterface.MainActivity;
|
||||||
import org.kde.kdeconnect_tp.R;
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
|
|
||||||
public class PingPlugin extends Plugin {
|
public class PingPlugin extends Plugin {
|
||||||
|
|
||||||
/*static {
|
|
||||||
PluginFactory.registerPlugin(PingPlugin.class);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPluginName() {
|
public String getPluginName() {
|
||||||
return "plugin_ping";
|
return "plugin_ping";
|
||||||
@@ -41,6 +41,11 @@ public class PingPlugin extends Plugin {
|
|||||||
return context.getResources().getDrawable(R.drawable.icon);
|
return context.getResources().getDrawable(R.drawable.icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledByDefault() {
|
public boolean isEnabledByDefault() {
|
||||||
return true;
|
return true;
|
||||||
@@ -63,17 +68,36 @@ public class PingPlugin extends Plugin {
|
|||||||
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_PING)) {
|
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_PING)) {
|
||||||
//Log.e("PingPackageReceiver", "was a 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
|
||||||
|
);
|
||||||
|
|
||||||
|
int id;
|
||||||
|
String message;
|
||||||
|
if (np.has("message")) {
|
||||||
|
message = np.getString("message");
|
||||||
|
id = (int)System.currentTimeMillis();
|
||||||
|
} else {
|
||||||
|
message = "Ping!";
|
||||||
|
id = 42; //A unique id to create only one notification
|
||||||
|
}
|
||||||
|
|
||||||
Notification noti = new NotificationCompat.Builder(context)
|
Notification noti = new NotificationCompat.Builder(context)
|
||||||
.setContentTitle(device.getName())
|
.setContentTitle(device.getName())
|
||||||
.setContentText("Ping!")
|
.setContentText(message)
|
||||||
.setTicker("Ping!")
|
.setContentIntent(resultPendingIntent)
|
||||||
|
.setTicker(message)
|
||||||
.setSmallIcon(android.R.drawable.ic_dialog_alert)
|
.setSmallIcon(android.R.drawable.ic_dialog_alert)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setDefaults(Notification.DEFAULT_SOUND)
|
.setDefaults(Notification.DEFAULT_ALL)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
notificationManager.notify(42 /*a unique id to create only one notification*/, noti);
|
notificationManager.notify(id, noti);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -81,7 +105,7 @@ public class PingPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AlertDialog getErrorDialog(Context baseContext) {
|
public AlertDialog getErrorDialog(Activity deviceActivity) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@@ -51,6 +51,12 @@ public abstract class Plugin {
|
|||||||
*/
|
*/
|
||||||
public abstract boolean isEnabledByDefault();
|
public abstract boolean isEnabledByDefault();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if this plugin needs an specific UI settings.
|
||||||
|
*/
|
||||||
|
public abstract boolean hasSettings();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the listeners and structures in your plugin.
|
* Initialize the listeners and structures in your plugin.
|
||||||
* Should return true if initialization was successful.
|
* Should return true if initialization was successful.
|
||||||
@@ -73,7 +79,7 @@ public abstract class Plugin {
|
|||||||
* If onCreate returns false, should create a dialog explaining
|
* If onCreate returns false, should create a dialog explaining
|
||||||
* the problem (and how to fix it, if possible) to the user.
|
* the problem (and how to fix it, if possible) to the user.
|
||||||
*/
|
*/
|
||||||
public abstract AlertDialog getErrorDialog(Context baseContext);
|
public abstract AlertDialog getErrorDialog(Activity deviceActivity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a button that will be displayed in the user interface
|
* Creates a button that will be displayed in the user interface
|
@@ -7,10 +7,13 @@ import android.util.Log;
|
|||||||
|
|
||||||
import org.kde.kdeconnect.Device;
|
import org.kde.kdeconnect.Device;
|
||||||
import org.kde.kdeconnect.Plugins.BatteryPlugin.BatteryPlugin;
|
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.ClibpoardPlugin.ClipboardPlugin;
|
||||||
import org.kde.kdeconnect.Plugins.MprisPlugin.MprisPlugin;
|
import org.kde.kdeconnect.Plugins.MprisPlugin.MprisPlugin;
|
||||||
import org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationsPlugin;
|
import org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationsPlugin;
|
||||||
import org.kde.kdeconnect.Plugins.PingPlugin.PingPlugin;
|
import org.kde.kdeconnect.Plugins.PingPlugin.PingPlugin;
|
||||||
|
import org.kde.kdeconnect.Plugins.SharePlugin.SharePlugin;
|
||||||
import org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin;
|
import org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -21,12 +24,14 @@ public class PluginFactory {
|
|||||||
|
|
||||||
public static class PluginInfo {
|
public static class PluginInfo {
|
||||||
|
|
||||||
public PluginInfo(String pluginName, String displayName, String description, Drawable icon, boolean enabledByDefault) {
|
public PluginInfo(String pluginName, String displayName, String description, Drawable icon,
|
||||||
|
boolean enabledByDefault, boolean hasSettings) {
|
||||||
this.pluginName = pluginName;
|
this.pluginName = pluginName;
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
this.enabledByDefault = enabledByDefault;
|
this.enabledByDefault = enabledByDefault;
|
||||||
|
this.hasSettings = hasSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPluginName() {
|
public String getPluginName() {
|
||||||
@@ -45,15 +50,18 @@ public class PluginFactory {
|
|||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasSettings() { return hasSettings; }
|
||||||
|
|
||||||
public boolean isEnabledByDefault() {
|
public boolean isEnabledByDefault() {
|
||||||
return enabledByDefault;
|
return enabledByDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String pluginName;
|
private final String pluginName;
|
||||||
private String displayName;
|
private final String displayName;
|
||||||
private String description;
|
private final String description;
|
||||||
private final Drawable icon;
|
private final Drawable icon;
|
||||||
private boolean enabledByDefault;
|
private final boolean enabledByDefault;
|
||||||
|
private final boolean hasSettings;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,13 +69,16 @@ public class PluginFactory {
|
|||||||
private static final Map<String, PluginInfo> availablePluginsInfo = new TreeMap<String, PluginInfo>();
|
private static final Map<String, PluginInfo> availablePluginsInfo = new TreeMap<String, PluginInfo>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
//TODO: Avoid this factory having to know every plugin
|
//TODO: Use reflection to find all subclasses of Plugin, instead of adding them manually
|
||||||
PluginFactory.registerPlugin(TelephonyPlugin.class);
|
PluginFactory.registerPlugin(TelephonyPlugin.class);
|
||||||
PluginFactory.registerPlugin(PingPlugin.class);
|
PluginFactory.registerPlugin(PingPlugin.class);
|
||||||
PluginFactory.registerPlugin(MprisPlugin.class);
|
PluginFactory.registerPlugin(MprisPlugin.class);
|
||||||
PluginFactory.registerPlugin(ClipboardPlugin.class);
|
PluginFactory.registerPlugin(ClipboardPlugin.class);
|
||||||
PluginFactory.registerPlugin(BatteryPlugin.class);
|
PluginFactory.registerPlugin(BatteryPlugin.class);
|
||||||
|
PluginFactory.registerPlugin(SftpPlugin.class);
|
||||||
PluginFactory.registerPlugin(NotificationsPlugin.class);
|
PluginFactory.registerPlugin(NotificationsPlugin.class);
|
||||||
|
PluginFactory.registerPlugin(MousePadPlugin.class);
|
||||||
|
PluginFactory.registerPlugin(SharePlugin.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PluginInfo getPluginInfo(Context context, String pluginName) {
|
public static PluginInfo getPluginInfo(Context context, String pluginName) {
|
||||||
@@ -76,7 +87,8 @@ public class PluginFactory {
|
|||||||
try {
|
try {
|
||||||
Plugin p = ((Plugin)availablePlugins.get(pluginName).newInstance());
|
Plugin p = ((Plugin)availablePlugins.get(pluginName).newInstance());
|
||||||
p.setContext(context, null);
|
p.setContext(context, null);
|
||||||
info = new PluginInfo(pluginName, p.getDisplayName(), p.getDescription(), p.getIcon(), p.isEnabledByDefault());
|
info = new PluginInfo(pluginName, p.getDisplayName(), p.getDescription(), p.getIcon(),
|
||||||
|
p.isEnabledByDefault(), p.hasSettings());
|
||||||
availablePluginsInfo.put(pluginName, info); //Cache it
|
availablePluginsInfo.put(pluginName, info); //Cache it
|
||||||
return info;
|
return info;
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
@@ -109,10 +121,10 @@ public class PluginFactory {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerPlugin(Class pluginClass) {
|
public static void registerPlugin(Class<? extends Plugin> pluginClass) {
|
||||||
try {
|
try {
|
||||||
//I hate this but I need to create an instance because abstract static functions can't be declared
|
//I hate this but I need to create an instance because abstract static functions can't be declared
|
||||||
String pluginName = ((Plugin)pluginClass.newInstance()).getPluginName();
|
String pluginName = (pluginClass.newInstance()).getPluginName();
|
||||||
availablePlugins.put(pluginName, pluginClass);
|
availablePlugins.put(pluginName, pluginClass);
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
Log.e("PluginFactory","addPlugin exception");
|
Log.e("PluginFactory","addPlugin exception");
|
@@ -0,0 +1,210 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,128 @@
|
|||||||
|
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.Helpers.StorageHelper;
|
||||||
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
import org.kde.kdeconnect.Plugins.Plugin;
|
||||||
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SftpPlugin extends Plugin {
|
||||||
|
|
||||||
|
private static final SimpleSftpServer server = new SimpleSftpServer();
|
||||||
|
|
||||||
|
@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 hasSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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());
|
||||||
|
|
||||||
|
//Kept for compatibility, but new desktop clients will read "multiPaths" instead,
|
||||||
|
// that supports devices with more than one external storage
|
||||||
|
np2.set("path", Environment.getExternalStorageDirectory().getAbsolutePath());
|
||||||
|
|
||||||
|
List<StorageHelper.StorageInfo> storageList = StorageHelper.getStorageList();
|
||||||
|
ArrayList<String> paths = new ArrayList<String>();
|
||||||
|
ArrayList<String> pathNames = new ArrayList<String>();
|
||||||
|
|
||||||
|
for (StorageHelper.StorageInfo storage : storageList) {
|
||||||
|
paths.add(storage.path);
|
||||||
|
StringBuilder res = new StringBuilder();
|
||||||
|
|
||||||
|
if (storageList.size() > 1) {
|
||||||
|
if (!storage.removable) {
|
||||||
|
res.append(context.getString(R.string.sftp_internal_storage));
|
||||||
|
} else if (storage.number > 1) {
|
||||||
|
res.append(context.getString(R.string.sftp_sdcard_num, storage.number));
|
||||||
|
} else {
|
||||||
|
res.append(context.getString(R.string.sftp_sdcard));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.append(context.getString(R.string.sftp_all_files));
|
||||||
|
}
|
||||||
|
if (storage.readonly) {
|
||||||
|
res.append(" ");
|
||||||
|
res.append(context.getString(R.string.sftp_readonly));
|
||||||
|
}
|
||||||
|
pathNames.add(res.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Shortcut for users that only want to browse camera pictures
|
||||||
|
String cameraDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera";
|
||||||
|
if (new File(cameraDir).exists()) {
|
||||||
|
paths.add(cameraDir);
|
||||||
|
pathNames.add(context.getString(R.string.sftp_camera));
|
||||||
|
}
|
||||||
|
|
||||||
|
np2.set("multiPaths", paths);
|
||||||
|
np2.set("pathNames", pathNames);
|
||||||
|
|
||||||
|
device.sendPackage(np2);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AlertDialog getErrorDialog(Activity deviceActivity) { return null; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Button getInterfaceButton(Activity activity) { return null; }
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,255 @@
|
|||||||
|
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.SharedPreferences;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPluginName() {
|
||||||
|
return "plugin_share";
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 hasSettings() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder = 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);
|
||||||
|
|
||||||
|
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
if (prefs.getBoolean("share_notification_preference", true)) {
|
||||||
|
builder.setDefaults(Notification.DEFAULT_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification noti = builder.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_ALL)
|
||||||
|
.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(Activity deviceActivity) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Button getInterfaceButton(Activity activity) { return null; }
|
||||||
|
|
||||||
|
}
|
@@ -10,7 +10,6 @@ import android.graphics.drawable.Drawable;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.telephony.SmsMessage;
|
import android.telephony.SmsMessage;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
|
||||||
import org.kde.kdeconnect.Helpers.ContactsHelper;
|
import org.kde.kdeconnect.Helpers.ContactsHelper;
|
||||||
@@ -20,10 +19,6 @@ import org.kde.kdeconnect_tp.R;
|
|||||||
|
|
||||||
public class TelephonyPlugin extends Plugin {
|
public class TelephonyPlugin extends Plugin {
|
||||||
|
|
||||||
/*static {
|
|
||||||
PluginFactory.registerPlugin(TelephonyPlugin.class);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPluginName() {
|
public String getPluginName() {
|
||||||
return "plugin_telephony";
|
return "plugin_telephony";
|
||||||
@@ -49,7 +44,12 @@ public class TelephonyPlugin extends Plugin {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BroadcastReceiver receiver = new BroadcastReceiver() {
|
@Override
|
||||||
|
public boolean hasSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
|
||||||
@@ -96,6 +96,8 @@ public class TelephonyPlugin extends Plugin {
|
|||||||
|
|
||||||
public void callBroadcastReceived(int state, String phoneNumber) {
|
public void callBroadcastReceived(int state, String phoneNumber) {
|
||||||
|
|
||||||
|
//Log.e("TelephonyPlugin", "callBroadcastReceived");
|
||||||
|
|
||||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_TELEPHONY);
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_TELEPHONY);
|
||||||
if (phoneNumber != null) {
|
if (phoneNumber != null) {
|
||||||
phoneNumber = ContactsHelper.phoneNumberLookup(context,phoneNumber);
|
phoneNumber = ContactsHelper.phoneNumberLookup(context,phoneNumber);
|
||||||
@@ -162,6 +164,7 @@ public class TelephonyPlugin extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
|
//Log.e("TelephonyPlugin", "onCreate");
|
||||||
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
|
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
|
||||||
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
|
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
|
||||||
context.registerReceiver(receiver, filter);
|
context.registerReceiver(receiver, filter);
|
||||||
@@ -180,7 +183,7 @@ public class TelephonyPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AlertDialog getErrorDialog(Context baseContext) {
|
public AlertDialog getErrorDialog(Activity deviceActivity) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@@ -0,0 +1,201 @@
|
|||||||
|
package org.kde.kdeconnect.UserInterface;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
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.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.CustomItem;
|
||||||
|
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||||
|
import org.kde.kdeconnect.UserInterface.List.SectionItem;
|
||||||
|
import org.kde.kdeconnect.UserInterface.List.SmallEntryItem;
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public static final int RESULT_NEEDS_RELOAD = Activity.RESULT_FIRST_USER;
|
||||||
|
|
||||||
|
TextView errorHeader;
|
||||||
|
|
||||||
|
private final Device.PluginsChangedListener pluginsChangedListener = new Device.PluginsChangedListener() {
|
||||||
|
@Override
|
||||||
|
public void onPluginsChanged(final Device device) {
|
||||||
|
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
ArrayList<ListAdapter.Item> items = new ArrayList<ListAdapter.Item>();
|
||||||
|
|
||||||
|
if (!device.isReachable()) {
|
||||||
|
//Not reachable, show unpair button
|
||||||
|
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));
|
||||||
|
} else {
|
||||||
|
//Plugins button list
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Failed plugins List
|
||||||
|
final Collection<Plugin> failed = device.getFailedPlugins().values();
|
||||||
|
if (!failed.isEmpty()) {
|
||||||
|
if (errorHeader == null) {
|
||||||
|
errorHeader = new TextView(DeviceActivity.this);
|
||||||
|
errorHeader.setPadding(0, 48, 0, 0);
|
||||||
|
errorHeader.setOnClickListener(null);
|
||||||
|
errorHeader.setOnLongClickListener(null);
|
||||||
|
errorHeader.setText(getResources().getString(R.string.plugins_failed_to_load));
|
||||||
|
}
|
||||||
|
items.add(new CustomItem(errorHeader));
|
||||||
|
for (final Plugin p : failed) {
|
||||||
|
items.add(new SmallEntryItem(p.getDisplayName(), new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
p.getErrorDialog(DeviceActivity.this).show();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView buttonsList = (ListView)findViewById(R.id.buttons_list);
|
||||||
|
ListAdapter adapter = new ListAdapter(DeviceActivity.this, items);
|
||||||
|
buttonsList.setAdapter(adapter);
|
||||||
|
|
||||||
|
} 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);
|
||||||
|
if (!device.hasPluginsLoaded()) {
|
||||||
|
device.reloadPluginsFromSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
BackgroundService.RunCommand(DeviceActivity.this, new BackgroundService.InstanceCallback() {
|
||||||
|
@Override
|
||||||
|
public void onServiceStart(BackgroundService service) {
|
||||||
|
Device device = service.getDevice(deviceId);
|
||||||
|
if (device == null) return;
|
||||||
|
device.removePluginsChangedListener(pluginsChangedListener);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
|
super.onPrepareOptionsMenu(menu);
|
||||||
|
menu.clear();
|
||||||
|
if (device != null && 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
switch (requestCode)
|
||||||
|
{
|
||||||
|
case RESULT_NEEDS_RELOAD:
|
||||||
|
BackgroundService.RunCommand(DeviceActivity.this, new BackgroundService.InstanceCallback() {
|
||||||
|
@Override
|
||||||
|
public void onServiceStart(BackgroundService service) {
|
||||||
|
Device device = service.getDevice(deviceId);
|
||||||
|
device.reloadPluginsFromSettings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
package org.kde.kdeconnect.UserInterface.List;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
public class CustomItem implements ListAdapter.Item {
|
||||||
|
|
||||||
|
private final View view;
|
||||||
|
|
||||||
|
public CustomItem(View v) {
|
||||||
|
this.view = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View inflateView(LayoutInflater layoutInflater) {
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -4,6 +4,7 @@ package org.kde.kdeconnect.UserInterface.List;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -24,10 +25,24 @@ public class DeviceItem implements ListAdapter.Item {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View inflateView(LayoutInflater layoutInflater) {
|
public View inflateView(LayoutInflater layoutInflater) {
|
||||||
View v = layoutInflater.inflate(R.layout.list_item_entry, null);
|
final View v = layoutInflater.inflate(R.layout.list_item_entry, null);
|
||||||
|
|
||||||
|
//Highlight when selected effect
|
||||||
|
v.setBackgroundDrawable(layoutInflater.getContext().getResources().getDrawable(R.drawable.kitkatcompatselector_list_selector_holo_dark));
|
||||||
|
|
||||||
TextView titleView = (TextView)v.findViewById(R.id.list_item_entry_title);
|
TextView titleView = (TextView)v.findViewById(R.id.list_item_entry_title);
|
||||||
if (titleView != null) titleView.setText(device.getName());
|
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() {
|
v.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
@@ -0,0 +1,27 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -14,8 +14,8 @@ public class ListAdapter extends ArrayAdapter<ListAdapter.Item> {
|
|||||||
public View inflateView(LayoutInflater layoutInflater);
|
public View inflateView(LayoutInflater layoutInflater);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<Item> items;
|
private final ArrayList<Item> items;
|
||||||
private LayoutInflater layoutInflater;
|
private final LayoutInflater layoutInflater;
|
||||||
|
|
||||||
public ListAdapter(Context context, ArrayList<Item> items) {
|
public ListAdapter(Context context, ArrayList<Item> items) {
|
||||||
super(context, 0, items);
|
super(context, 0, items);
|
@@ -9,10 +9,11 @@ import org.kde.kdeconnect_tp.R;
|
|||||||
public class SectionItem implements ListAdapter.Item {
|
public class SectionItem implements ListAdapter.Item {
|
||||||
|
|
||||||
private final String title;
|
private final String title;
|
||||||
public boolean isEmpty;
|
public boolean isSectionEmpty;
|
||||||
|
|
||||||
public SectionItem(String title) {
|
public SectionItem(String title) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
this.isSectionEmpty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -20,14 +21,14 @@ public class SectionItem implements ListAdapter.Item {
|
|||||||
|
|
||||||
View v = layoutInflater.inflate(R.layout.list_item_category, null);
|
View v = layoutInflater.inflate(R.layout.list_item_category, null);
|
||||||
|
|
||||||
|
//Make it not selectable
|
||||||
v.setOnClickListener(null);
|
v.setOnClickListener(null);
|
||||||
v.setOnLongClickListener(null);
|
v.setOnLongClickListener(null);
|
||||||
v.setLongClickable(false);
|
|
||||||
|
|
||||||
TextView sectionView = (TextView) v.findViewById(R.id.list_item_category_text);
|
TextView sectionView = (TextView) v.findViewById(R.id.list_item_category_text);
|
||||||
sectionView.setText(title);
|
sectionView.setText(title);
|
||||||
|
|
||||||
if (isEmpty) {
|
if (isSectionEmpty) {
|
||||||
v.findViewById(R.id.list_item_category_empty_placeholder).setVisibility(View.VISIBLE);
|
v.findViewById(R.id.list_item_category_empty_placeholder).setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
@@ -0,0 +1,40 @@
|
|||||||
|
package org.kde.kdeconnect.UserInterface.List;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
|
public class SmallEntryItem implements ListAdapter.Item {
|
||||||
|
|
||||||
|
private final String title;
|
||||||
|
private final View.OnClickListener clickListener;
|
||||||
|
|
||||||
|
public SmallEntryItem(String title) {
|
||||||
|
this.title = title;
|
||||||
|
this.clickListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SmallEntryItem(String title, View.OnClickListener clickListener) {
|
||||||
|
this.title = title;
|
||||||
|
this.clickListener = clickListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View inflateView(LayoutInflater layoutInflater) {
|
||||||
|
View v = layoutInflater.inflate(android.R.layout.simple_list_item_1, null);
|
||||||
|
|
||||||
|
TextView titleView = (TextView)v.findViewById(android.R.id.text1);
|
||||||
|
if (titleView != null) titleView.setText(title);
|
||||||
|
if (clickListener != null) {
|
||||||
|
titleView.setOnClickListener(clickListener);
|
||||||
|
v.setBackgroundDrawable(layoutInflater.getContext().getResources().getDrawable(R.drawable.kitkatcompatselector_list_selector_holo_dark));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,27 @@
|
|||||||
|
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,11 +1,11 @@
|
|||||||
package org.kde.kdeconnect.UserInterface;
|
package org.kde.kdeconnect.UserInterface;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@@ -66,6 +66,9 @@ public class MainActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
break;
|
break;
|
||||||
|
case R.id.menu_settings:
|
||||||
|
startActivity(new Intent(this,MainSettingsActivity.class));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -79,7 +82,6 @@ public class MainActivity extends ActionBarActivity {
|
|||||||
|
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
|
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -102,36 +104,36 @@ public class MainActivity extends ActionBarActivity {
|
|||||||
Resources res = getResources();
|
Resources res = getResources();
|
||||||
|
|
||||||
section = new SectionItem(res.getString(R.string.category_connected_devices));
|
section = new SectionItem(res.getString(R.string.category_connected_devices));
|
||||||
section.isEmpty = true;
|
section.isSectionEmpty = true;
|
||||||
items.add(section);
|
items.add(section);
|
||||||
for(Device d : devices) {
|
for(Device d : devices) {
|
||||||
if (d.isReachable() && d.isPaired()) {
|
if (d.isReachable() && d.isPaired()) {
|
||||||
items.add(new DeviceItem(MainActivity.this, d));
|
items.add(new DeviceItem(MainActivity.this, d));
|
||||||
section.isEmpty = false;
|
section.isSectionEmpty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section = new SectionItem(res.getString(R.string.category_not_paired_devices));
|
section = new SectionItem(res.getString(R.string.category_not_paired_devices));
|
||||||
section.isEmpty = true;
|
section.isSectionEmpty = true;
|
||||||
items.add(section);
|
items.add(section);
|
||||||
for(Device d : devices) {
|
for(Device d : devices) {
|
||||||
if (d.isReachable() && !d.isPaired()) {
|
if (d.isReachable() && !d.isPaired()) {
|
||||||
items.add(new DeviceItem(MainActivity.this, d));
|
items.add(new DeviceItem(MainActivity.this, d));
|
||||||
section.isEmpty = false;
|
section.isSectionEmpty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section = new SectionItem(res.getString(R.string.category_remembered_devices));
|
section = new SectionItem(res.getString(R.string.category_remembered_devices));
|
||||||
section.isEmpty = true;
|
section.isSectionEmpty = true;
|
||||||
items.add(section);
|
items.add(section);
|
||||||
for(Device d : devices) {
|
for(Device d : devices) {
|
||||||
if (!d.isReachable() && d.isPaired()) {
|
if (!d.isReachable() && d.isPaired()) {
|
||||||
items.add(new DeviceItem(MainActivity.this, d));
|
items.add(new DeviceItem(MainActivity.this, d));
|
||||||
section.isEmpty = false;
|
section.isSectionEmpty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (section.isEmpty) {
|
if (section.isSectionEmpty) {
|
||||||
items.remove(items.size()-1); //Remove section
|
items.remove(items.size()-1); //Remove remembered devices section if empty
|
||||||
}
|
}
|
||||||
|
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@@ -142,12 +144,13 @@ public class MainActivity extends ActionBarActivity {
|
|||||||
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||||
view.callOnClick();
|
view.performClick();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
@@ -0,0 +1,112 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,35 @@
|
|||||||
|
package org.kde.kdeconnect.UserInterface;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
|
public class MaxWidthImageButton extends ImageButton {
|
||||||
|
|
||||||
|
int mMaxWidth = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
public MaxWidthImageButton(Context context) {
|
||||||
|
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MaxWidthImageButton(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MaxWidthImageButton);
|
||||||
|
mMaxWidth = a.getDimensionPixelSize(R.styleable.MaxWidthImageButton_maxWidth, Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
if(getMeasuredWidth() > mMaxWidth){
|
||||||
|
setMeasuredDimension(mMaxWidth, getMeasuredHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -18,7 +18,7 @@ public class PairActivity extends ActionBarActivity {
|
|||||||
private String deviceId;
|
private String deviceId;
|
||||||
private Device device = null;
|
private Device device = null;
|
||||||
|
|
||||||
private Device.PairingCallback pairingCallback = new Device.PairingCallback() {
|
private final Device.PairingCallback pairingCallback = new Device.PairingCallback() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void incomingRequest() {
|
public void incomingRequest() {
|
||||||
@@ -76,6 +76,7 @@ public class PairActivity extends ActionBarActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onServiceStart(BackgroundService service) {
|
public void onServiceStart(BackgroundService service) {
|
||||||
device = service.getDevice(deviceId);
|
device = service.getDevice(deviceId);
|
||||||
|
if (device == null) return;
|
||||||
setTitle(device.getName());
|
setTitle(device.getName());
|
||||||
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
notificationManager.cancel(device.getNotificationId());
|
notificationManager.cancel(device.getNotificationId());
|
||||||
@@ -94,6 +95,7 @@ public class PairActivity extends ActionBarActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onServiceStart(BackgroundService service) {
|
public void onServiceStart(BackgroundService service) {
|
||||||
device = service.getDevice(deviceId);
|
device = service.getDevice(deviceId);
|
||||||
|
if (device == null) return;
|
||||||
device.requestPairing();
|
device.requestPairing();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -134,6 +136,8 @@ public class PairActivity extends ActionBarActivity {
|
|||||||
BackgroundService.RunCommand(PairActivity.this, new BackgroundService.InstanceCallback() {
|
BackgroundService.RunCommand(PairActivity.this, new BackgroundService.InstanceCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onServiceStart(BackgroundService service) {
|
public void onServiceStart(BackgroundService service) {
|
||||||
|
device = service.getDevice(deviceId);
|
||||||
|
if (device == null) return;
|
||||||
device.addPairingCallback(pairingCallback);
|
device.addPairingCallback(pairingCallback);
|
||||||
}
|
}
|
||||||
});
|
});
|
@@ -0,0 +1,17 @@
|
|||||||
|
package org.kde.kdeconnect.UserInterface;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceActivity;
|
||||||
|
|
||||||
|
public class PluginSettingsActivity extends PreferenceActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
String resource_name = getIntent().getStringExtra(Intent.EXTRA_INTENT);
|
||||||
|
int resource_file = getResources().getIdentifier(resource_name, "xml", getPackageName());
|
||||||
|
addPreferencesFromResource(resource_file);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,88 @@
|
|||||||
|
package org.kde.kdeconnect.UserInterface;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.CheckBoxPreference;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.preference.PreferenceActivity;
|
||||||
|
import android.preference.PreferenceScreen;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.BackgroundService;
|
||||||
|
import org.kde.kdeconnect.Device;
|
||||||
|
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||||
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class SettingsActivity extends PreferenceActivity {
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(this);
|
||||||
|
setPreferenceScreen(preferenceScreen);
|
||||||
|
|
||||||
|
if (getIntent().hasExtra("deviceId")) {
|
||||||
|
deviceId = getIntent().getStringExtra("deviceId");
|
||||||
|
}
|
||||||
|
|
||||||
|
BackgroundService.RunCommand(getApplicationContext(), new BackgroundService.InstanceCallback() {
|
||||||
|
@Override
|
||||||
|
public void onServiceStart(BackgroundService service) {
|
||||||
|
|
||||||
|
final Device device = service.getDevice(deviceId);
|
||||||
|
Set<String> plugins = PluginFactory.getAvailablePlugins();
|
||||||
|
|
||||||
|
final ArrayList<Preference> preferences = new ArrayList<Preference>();
|
||||||
|
for (final String pluginName : plugins) {
|
||||||
|
final CheckBoxPreference pref = new CheckBoxPreference(getBaseContext());
|
||||||
|
|
||||||
|
PluginFactory.PluginInfo info = PluginFactory.getPluginInfo(getBaseContext(), pluginName);
|
||||||
|
pref.setKey(pluginName);
|
||||||
|
pref.setTitle(info.getDisplayName());
|
||||||
|
pref.setSummary(info.getDescription());
|
||||||
|
pref.setChecked(device.isPluginEnabled(pluginName));
|
||||||
|
preferences.add(pref);
|
||||||
|
preferenceScreen.addPreference(pref);
|
||||||
|
|
||||||
|
if (info.hasSettings()) {
|
||||||
|
final Preference pluginPreference = new Preference(getBaseContext());
|
||||||
|
pluginPreference.setKey(pluginName + getString(R.string.plugin_settings_key));
|
||||||
|
pluginPreference.setTitle(info.getDisplayName());
|
||||||
|
pluginPreference.setSummary(R.string.plugin_settings);
|
||||||
|
preferences.add(pluginPreference);
|
||||||
|
preferenceScreen.addPreference(pluginPreference);
|
||||||
|
pluginPreference.setDependency(pref.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||||
|
Preference pref = preferences.get(i);
|
||||||
|
if (pref.getDependency() == null) { //Is a plugin check
|
||||||
|
CheckBoxPreference check = (CheckBoxPreference)pref;
|
||||||
|
boolean enabled = device.isPluginEnabled(pref.getKey());
|
||||||
|
device.setPluginEnabled(pref.getKey(), !enabled);
|
||||||
|
check.setChecked(!enabled);
|
||||||
|
} else { //Is a plugin suboption
|
||||||
|
if (pref.isEnabled()) {
|
||||||
|
Intent intent = new Intent(SettingsActivity.this, PluginSettingsActivity.class);
|
||||||
|
intent.putExtra(Intent.EXTRA_INTENT, pref.getKey());
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,303 @@
|
|||||||
|
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) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("ShareToReceiver", "Exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
} 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 unused) {
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
BIN
src/main/res/drawable-hdpi/ic_action_keyboard.png
Normal file
After Width: | Height: | Size: 351 B |
BIN
src/main/res/drawable-hdpi/ic_action_refresh.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/main/res/drawable-hdpi/ic_action_settings.png
Normal file
After Width: | Height: | Size: 557 B |
BIN
src/main/res/drawable-hdpi/ic_volume.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/main/res/drawable-hdpi/icon.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 115 B |
After Width: | Height: | Size: 139 B |
After Width: | Height: | Size: 115 B |
After Width: | Height: | Size: 115 B |
After Width: | Height: | Size: 189 B |
BIN
src/main/res/drawable-ldpi/icon.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/main/res/drawable-mdpi/ic_action_keyboard.png
Normal file
After Width: | Height: | Size: 431 B |
After Width: | Height: | Size: 110 B |
After Width: | Height: | Size: 117 B |
After Width: | Height: | Size: 110 B |
After Width: | Height: | Size: 110 B |
After Width: | Height: | Size: 172 B |
BIN
src/main/res/drawable-xhdpi/ic_action_keyboard.png
Normal file
After Width: | Height: | Size: 451 B |
BIN
src/main/res/drawable-xhdpi/ic_action_refresh.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/main/res/drawable-xhdpi/ic_action_settings.png
Normal file
After Width: | Height: | Size: 713 B |