From 529c1e7890bb11cb0f9d5d51b4d02365dbfa92fc Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Mon, 31 Oct 2016 17:02:46 +0800 Subject: [PATCH] upload 1.0.0 --- .gitignore | 37 + LICENSE | 674 +++++++++++++++ README.md | 52 ++ app/.gitignore | 1 + app/build.gradle | 52 ++ app/proguard-rules.pro | 65 ++ app/src/main/AndroidManifest.xml | 52 ++ .../android/app/mirror/app/MainActivity.java | 788 ++++++++++++++++++ .../android/app/mirror/app/MirrorService.java | 392 +++++++++ .../mirror/event/ArtboardSelectedEvent.java | 37 + .../app/mirror/event/MirrorFoundEvent.java | 37 + .../app/mirror/event/MirrorLostEvent.java | 37 + .../app/mirror/event/MirrorMessageEvent.java | 37 + .../event/MirrorResolveFailedEvent.java | 37 + .../event/MirrorResolveSuccessEvent.java | 37 + .../app/mirror/event/MirrorSelectedEvent.java | 37 + .../app/mirror/event/ViewSwitchedEvent.java | 52 ++ .../app/mirror/event/WebSocketCloseEvent.java | 45 + .../mirror/event/WebSocketFailureEvent.java | 49 ++ .../app/mirror/event/WifiConnectedEvent.java | 21 + .../app/mirror/event/WifiConnectingEvent.java | 21 + .../mirror/event/WifiDisconnectedEvent.java | 21 + .../android/app/mirror/model/Artboard.java | 133 +++ .../android/app/mirror/model/Content.java | 177 ++++ .../android/app/mirror/model/Contents.java | 34 + .../android/app/mirror/model/Handshake.java | 56 ++ .../android/app/mirror/model/Message.java | 45 + .../android/app/mirror/model/Metadata.java | 45 + .../android/app/mirror/model/MirrorInfo.java | 96 +++ .../zhihu/android/app/mirror/model/Page.java | 67 ++ .../android/app/mirror/model/Screen.java | 67 ++ .../android/app/mirror/util/AnimUtils.java | 56 ++ .../android/app/mirror/util/DisplayUtils.java | 91 ++ .../android/app/mirror/util/MirrorUtils.java | 124 +++ .../android/app/mirror/util/NetworkUtils.java | 46 + .../app/mirror/util/PreferenceUtils.java | 75 ++ .../zhihu/android/app/mirror/util/RxBus.java | 48 ++ .../app/mirror/weiget/ArtboardLayout.java | 177 ++++ .../app/mirror/weiget/ArtboardPagerView.java | 64 ++ .../app/mirror/weiget/ArtboardView.java | 210 +++++ .../mirror/weiget/ConnectStatusLayout.java | 88 ++ .../app/mirror/weiget/DownsamplingView.java | 123 +++ .../app/mirror/weiget/MirrorNameView.java | 89 ++ .../android/app/mirror/weiget/PointView.java | 119 +++ .../weiget/adapter/ArtboardListAdapter.java | 83 ++ .../weiget/adapter/ArtboardPagerAdapter.java | 127 +++ .../weiget/adapter/MirrorListAdapter.java | 83 ++ .../mirror/weiget/holder/ArtboardHolder.java | 38 + .../weiget/holder/DownsamplingHolder.java | 71 ++ .../weiget/holder/MirrorInfoHolder.java | 52 ++ .../app/mirror/weiget/holder/PlaceHolder.java | 36 + app/src/main/res/layout/activity_main.xml | 28 + .../main/res/layout/layout_artboard_list.xml | 27 + .../main/res/layout/layout_artboard_pager.xml | 26 + .../main/res/layout/layout_connect_status.xml | 51 ++ .../main/res/layout/pager_item_artboard.xml | 44 + .../res/layout/recycler_item_downsampling.xml | 31 + .../main/res/layout/recycler_item_mirror.xml | 40 + .../main/res/layout/recycler_item_place.xml | 27 + app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2270 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1356 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 3312 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 5953 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9304 bytes app/src/main/res/values-v23/dimens.xml | 25 + app/src/main/res/values-zh-rCN/strings.xml | 31 + app/src/main/res/values-zh-rHK/strings.xml | 31 + app/src/main/res/values-zh-rMO/strings.xml | 31 + app/src/main/res/values-zh-rTW/strings.xml | 31 + app/src/main/res/values/attrs.xml | 35 + app/src/main/res/values/colors.xml | 27 + app/src/main/res/values/dimens.xml | 25 + app/src/main/res/values/ids.xml | 26 + app/src/main/res/values/integers.xml | 26 + app/src/main/res/values/strings.xml | 31 + app/src/main/res/values/styles.xml | 29 + build.gradle | 37 + example.png | Bin 0 -> 78296 bytes gradle.properties | 35 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes gradle/wrapper/gradle-wrapper.properties | 24 + gradlew | 160 ++++ gradlew.bat | 90 ++ settings.gradle | 19 + 84 files changed, 6088 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/app/MainActivity.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/app/MirrorService.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/ArtboardSelectedEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/MirrorFoundEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/MirrorLostEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/MirrorMessageEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/MirrorResolveFailedEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/MirrorResolveSuccessEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/MirrorSelectedEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/ViewSwitchedEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/WebSocketCloseEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/WebSocketFailureEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/WifiConnectedEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/WifiConnectingEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/event/WifiDisconnectedEvent.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/model/Artboard.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/model/Content.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/model/Contents.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/model/Handshake.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/model/Message.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/model/Metadata.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/model/MirrorInfo.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/model/Page.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/model/Screen.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/util/AnimUtils.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/util/DisplayUtils.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/util/MirrorUtils.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/util/NetworkUtils.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/util/PreferenceUtils.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/util/RxBus.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/ArtboardLayout.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/ArtboardPagerView.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/ArtboardView.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/ConnectStatusLayout.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/DownsamplingView.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/MirrorNameView.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/PointView.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/adapter/ArtboardListAdapter.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/adapter/ArtboardPagerAdapter.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/adapter/MirrorListAdapter.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/ArtboardHolder.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/DownsamplingHolder.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/MirrorInfoHolder.java create mode 100644 app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/PlaceHolder.java create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/layout_artboard_list.xml create mode 100644 app/src/main/res/layout/layout_artboard_pager.xml create mode 100644 app/src/main/res/layout/layout_connect_status.xml create mode 100644 app/src/main/res/layout/pager_item_artboard.xml create mode 100644 app/src/main/res/layout/recycler_item_downsampling.xml create mode 100644 app/src/main/res/layout/recycler_item_mirror.xml create mode 100644 app/src/main/res/layout/recycler_item_place.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/values-v23/dimens.xml create mode 100644 app/src/main/res/values-zh-rCN/strings.xml create mode 100644 app/src/main/res/values-zh-rHK/strings.xml create mode 100644 app/src/main/res/values-zh-rMO/strings.xml create mode 100644 app/src/main/res/values-zh-rTW/strings.xml create mode 100644 app/src/main/res/values/attrs.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/dimens.xml create mode 100644 app/src/main/res/values/ids.xml create mode 100644 app/src/main/res/values/integers.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 build.gradle create mode 100644 example.png create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe40401 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# Built application files +*.apk +*.ap_ + +# Files for the Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# Others +.idea/ +*.DS_Store +*.iml \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f748911 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + Mirror - Yet another Sketch Mirror App for Android. + Copyright (C) 2016 Zhihu Inc. + + 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Mirror Copyright (C) 2016 Zhihu Inc. + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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. But first, please read +. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..84751c4 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +Mirror +=== + +Yet another Sketch Mirror App for Android. + +Support Android Lollipop+. + +## Usage + +![example.png](./example.png "example.png") + +## Attendtion + + - Sometimes the WIFI connection is unstable, better to use it at good network. USB supports comming soon. + + - Highly recommended to use resolution 1280x720's phone to preview Material Design; or resolution : artboard's size = 1:1. + + - Good Pull Request is always welcome :) + +## Dependences + + - [Android Support Library](https://developer.android.com/topic/libraries/support-library/index.html "Support Library"), Apache License Version 2.0 + + - [ReactiveX/RxJava](https://github.com/ReactiveX/RxJava "ReactiveX/RxJava"), Apache License Version 2.0 + + - [trello/RxLifecycle](https://github.com/trello/RxLifecycle "trello/RxLifecycle"), Apache License Version 2.0 + + - [facebook/fresco](https://github.com/facebook/fresco "facebook/fresco"), BSD License + + - [google/gson](https://github.com/google/gson "google/gson"), Apache License Version 2.0 + + - [square/okhttp](https://github.com/square/okhttp "square/okhttp"), Apache License Version 2.0 + + - [davemorrissey/subsampling-scale-image-view](https://github.com/davemorrissey/subsampling-scale-image-view "davemorrissey/subsampling-scale-image-view"), Apache License Version 2.0 + +## License + + Mirror - Yet another Sketch Mirror App for Android. + Copyright (C) 2016 Zhihu Inc. + + 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 3 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, see . diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..d2ecfc0 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,52 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.0" + + defaultConfig { + applicationId "com.zhihu.android.app.mirror" + minSdkVersion 21 + targetSdkVersion 25 + versionCode 1 + versionName "1.0.0" + } + + buildTypes { + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + compile 'com.android.support:appcompat-v7:25.0.0' + compile 'com.android.support:recyclerview-v7:25.0.0' + compile 'io.reactivex:rxjava:1.2.1' + compile 'com.trello:rxlifecycle-components:0.8.0' + compile 'com.facebook.fresco:fresco:0.14.1' + compile 'com.facebook.fresco:imagepipeline-okhttp3:0.14.1' + compile 'com.google.code.gson:gson:2.7' + compile 'com.squareup.okhttp3:okhttp-ws:3.4.1' + compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.5.0' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..1c2ebdc --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,65 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/matthew/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# RxJava +# https://github.com/artem-zinnatullin/RxJavaProGuardRules +-dontwarn sun.misc.** +-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { + long producerIndex; + long consumerIndex; +} +-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { + rx.internal.util.atomic.LinkedQueueNode producerNode; +} +-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { + rx.internal.util.atomic.LinkedQueueNode consumerNode; +} + +# Fresco +# http://frescolib.org/docs/proguard.html +-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip +-keep @com.facebook.common.internal.DoNotStrip class * +-keepclassmembers class * { + @com.facebook.common.internal.DoNotStrip *; +} +-keepclassmembers class * { + native ; +} +-dontwarn okio.** +-dontwarn com.squareup.okhttp.** +-dontwarn okhttp3.** +-dontwarn javax.annotation.** +-dontwarn com.android.volley.toolbox.** + +# Gson +# https://github.com/google/gson/tree/master/examples/android-proguard-example +-keepattributes Signature +-keepattributes *Annotation* +-keep class sun.misc.Unsafe { *; } +-keep class com.google.gson.examples.android.model.** { *; } +-keep class * implements com.google.gson.TypeAdapterFactory +-keep class * implements com.google.gson.JsonSerializer +-keep class * implements com.google.gson.JsonDeserializer + +# OkHttp3 +# https://github.com/square/okhttp/issues/2230 +-keepattributes Signature +-keepattributes *Annotation* +-keep class okhttp3.** { *; } +-keep interface okhttp3.** { *; } +-dontwarn okhttp3.** \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..dfa838b --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/zhihu/android/app/mirror/app/MainActivity.java b/app/src/main/java/com/zhihu/android/app/mirror/app/MainActivity.java new file mode 100644 index 0000000..b5a1890 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/app/MainActivity.java @@ -0,0 +1,788 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.app; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.TimeInterpolator; +import android.app.ActivityManager; +import android.content.Intent; +import android.graphics.BitmapFactory; +import android.os.Bundle; +import android.support.v4.content.ContextCompat; +import android.support.v7.util.DiffUtil; +import android.support.v7.util.ListUpdateCallback; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; +import android.view.View; +import android.view.WindowManager; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.LinearInterpolator; +import android.widget.FrameLayout; + +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory; +import com.facebook.imagepipeline.core.ImagePipelineConfig; +import com.trello.rxlifecycle.android.ActivityEvent; +import com.trello.rxlifecycle.components.support.RxAppCompatActivity; + +import java.util.LinkedList; +import java.util.List; + +import com.zhihu.android.app.mirror.R; +import com.zhihu.android.app.mirror.event.ArtboardSelectedEvent; +import com.zhihu.android.app.mirror.event.ViewSwitchedEvent; +import com.zhihu.android.app.mirror.event.MirrorFoundEvent; +import com.zhihu.android.app.mirror.event.MirrorLostEvent; +import com.zhihu.android.app.mirror.event.MirrorMessageEvent; +import com.zhihu.android.app.mirror.event.MirrorResolveFailedEvent; +import com.zhihu.android.app.mirror.event.MirrorResolveSuccessEvent; +import com.zhihu.android.app.mirror.event.WebSocketCloseEvent; +import com.zhihu.android.app.mirror.event.WebSocketFailureEvent; +import com.zhihu.android.app.mirror.event.WifiConnectedEvent; +import com.zhihu.android.app.mirror.event.WifiConnectingEvent; +import com.zhihu.android.app.mirror.event.WifiDisconnectedEvent; +import com.zhihu.android.app.mirror.model.Artboard; +import com.zhihu.android.app.mirror.model.Content; +import com.zhihu.android.app.mirror.model.Message; +import com.zhihu.android.app.mirror.model.MirrorInfo; +import com.zhihu.android.app.mirror.util.AnimUtils; +import com.zhihu.android.app.mirror.util.DisplayUtils; +import com.zhihu.android.app.mirror.util.MirrorUtils; +import com.zhihu.android.app.mirror.util.NetworkUtils; +import com.zhihu.android.app.mirror.util.RxBus; +import com.zhihu.android.app.mirror.weiget.ArtboardLayout; +import com.zhihu.android.app.mirror.weiget.ArtboardPagerView; +import com.zhihu.android.app.mirror.weiget.ArtboardView; +import com.zhihu.android.app.mirror.weiget.ConnectStatusLayout; +import com.zhihu.android.app.mirror.weiget.adapter.ArtboardListAdapter; +import com.zhihu.android.app.mirror.weiget.adapter.ArtboardPagerAdapter; +import com.zhihu.android.app.mirror.weiget.adapter.MirrorListAdapter; +import okhttp3.OkHttpClient; +import rx.Subscriber; +import rx.android.schedulers.AndroidSchedulers; + +public class MainActivity extends RxAppCompatActivity implements ArtboardPagerView.ArtboardPagerViewDelegate, + ArtboardView.ArtboardViewCallback { + private static final int FLAG_MIRROR_LIST = ViewSwitchedEvent.FLAG_MIRROR_LIST; + private static final int FLAG_ARTBOARD_LIST = ViewSwitchedEvent.FLAG_ARTBOARD_LIST; + private static final int FLAG_ARTBOARD_PAGER = ViewSwitchedEvent.FLAG_ARTBOARD_PAGER; + + private FrameLayout mRootLayout; + private LinearInterpolator mLinearInterpolator; + private AccelerateInterpolator mAccelerateInterpolator; + private String mCurrentManifestId; + private int mCurrentFlag; + + private ConnectStatusLayout mConnectStatusLayout; + private String mCurrentSSID; + private MirrorInfo mCurrentMirror; + + private RecyclerView mMirrorListView; + private MirrorListAdapter mMirrorListAdapter; + private List mMirrorInfoList; + + private RecyclerView mArtboardListView; + private ArtboardListAdapter mArtboardListAdapter; + private List mArtboardList; + + // share mArtboardList with mArtboardListView + private ArtboardPagerView mArtboardPagerView; + private ArtboardPagerAdapter mArtboardPagerAdapter; + private boolean mIsDragDismiss; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + setTaskDescription(new ActivityManager.TaskDescription( + getString(R.string.app_name), + BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher), + ContextCompat.getColor(this, R.color.black))); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + mRootLayout = (FrameLayout) findViewById(R.id.root); + mLinearInterpolator = new LinearInterpolator(); + mAccelerateInterpolator = new AccelerateInterpolator(); + mCurrentFlag = FLAG_MIRROR_LIST; + + setupFresco(); + setupMirrorListView(); + setupArtboardListView(); + setupArtboardPagerView(); + setupConnectStatusLayout(); + setupRxBus(); + + Intent intent = new Intent(this, MirrorService.class); + startService(intent); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + Fresco.shutDown(); + + Intent intent = new Intent(this, MirrorService.class); + stopService(intent); + } + + // ============================================================================================= + + private void setupFresco() { + OkHttpClient client = new OkHttpClient.Builder().build(); + ImagePipelineConfig config = OkHttpImagePipelineConfigFactory.newBuilder(this, client) + .setResizeAndRotateEnabledForNetwork(true) + .setDownsampleEnabled(true) + .build(); + Fresco.initialize(this, config); + } + + private void setupMirrorListView() { + mMirrorInfoList = new LinkedList<>(); + mMirrorListAdapter = new MirrorListAdapter(this, mMirrorInfoList); + mMirrorListView = (RecyclerView) getLayoutInflater().inflate(R.layout.layout_artboard_list, + mRootLayout, false); + mMirrorListView.setAdapter(mMirrorListAdapter); + mMirrorListView.setLayoutManager(new LinearLayoutManager(this)); + + mMirrorListView.setVisibility(View.VISIBLE); + mRootLayout.addView(mMirrorListView); + } + + private void setupArtboardListView() { + mArtboardList = new LinkedList<>(); + mArtboardListAdapter = new ArtboardListAdapter(this, mArtboardList); + mArtboardListView = (RecyclerView) getLayoutInflater().inflate(R.layout.layout_artboard_list, + mRootLayout, false); + mArtboardListView.setAdapter(mArtboardListAdapter); + + final int gridSpanCount = getResources().getInteger(R.integer.grid_span_count); + GridLayoutManager layoutManager = new GridLayoutManager(this, gridSpanCount); + layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + return position <= 0 || position > mArtboardList.size() ? gridSpanCount : 1; + } + }); + mArtboardListView.setLayoutManager(layoutManager); + + mArtboardListView.setVisibility(View.INVISIBLE); + mRootLayout.addView(mArtboardListView); + } + + private void setupArtboardPagerView() { + mArtboardPagerAdapter = new ArtboardPagerAdapter(mArtboardList); + mArtboardPagerAdapter.setArtboardViewCallback(this); + mArtboardPagerView = (ArtboardPagerView) getLayoutInflater().inflate(R.layout.layout_artboard_pager, + mRootLayout, false); + mArtboardPagerView.setAdapter(mArtboardPagerAdapter); + mArtboardPagerView.setArtboardPagerViewDelegate(this); + mArtboardPagerView.setOffscreenPageLimit(getResources().getInteger(R.integer.artboard_pager_limit)); + mArtboardPagerView.setPageMargin(getResources().getDimensionPixelSize(R.dimen.view_pager_margin)); + + mArtboardPagerView.setVisibility(View.INVISIBLE); + mRootLayout.addView(mArtboardPagerView); + } + + @Override + public boolean isArtboardPagerViewSwipeEnable() { + ArtboardLayout layout = mArtboardPagerAdapter.getCurrentLayout(); + return layout != null && !layout.isScaling(); + } + + @Override + public void onDrag(ArtboardView view, float dragDismissDistance, float dragTo) { + dragTo = Math.abs(dragTo); + if (dragTo > dragDismissDistance) { + dragTo = dragDismissDistance; + } + + int alpha = (int) (255.0F - 51.0F * dragTo / dragDismissDistance); + mArtboardPagerView.getBackground().mutate().setAlpha(alpha); + } + + @Override + public void onDragDismiss(final ArtboardView view, boolean isDragDown) { + mIsDragDismiss = true; + switchToArtboardListView(); + view.animate().alpha(0.0F) + .translationY(isDragDown ? -view.getHeight() : view.getHeight()) + .setDuration(AnimUtils.DURATION) + .setInterpolator(new AccelerateInterpolator()) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + mIsDragDismiss = false; + mArtboardPagerView.getBackground().mutate().setAlpha(255); + view.setTranslationY(0.0F); + view.setAlpha(1.0F); + } + }) + .start(); + } + + private void setupConnectStatusLayout() { + mCurrentSSID = NetworkUtils.getCurrentSSID(this); + mConnectStatusLayout = (ConnectStatusLayout) getLayoutInflater().inflate(R.layout.layout_connect_status, + mRootLayout, false); + mConnectStatusLayout.setConnectStatus(ConnectStatusLayout.CONNECTING, mCurrentSSID); + + mConnectStatusLayout.setVisibility(View.VISIBLE); + mRootLayout.addView(mConnectStatusLayout); + } + + // ============================================================================================= + + @Override + public void onBackPressed() { + if (mCurrentFlag == FLAG_ARTBOARD_PAGER) { + switchToArtboardListView(); + } else if (mCurrentFlag == FLAG_ARTBOARD_LIST) { + switchToMirrorListView(); + } else { + super.onBackPressed(); + } + } + + private void switchToMirrorListView() { + if (mCurrentFlag == FLAG_ARTBOARD_LIST) { + AnimUtils.showViewAlphaAnim(mArtboardListView, mLinearInterpolator, false); + } else if (mCurrentFlag == FLAG_ARTBOARD_PAGER) { + AnimUtils.showViewAlphaAnim(mArtboardPagerView, mLinearInterpolator, false); + AnimUtils.showViewAlphaAnim(mArtboardListView, mLinearInterpolator, false); + AnimUtils.showViewAlphaAnim(mConnectStatusLayout, mLinearInterpolator, true); + DisplayUtils.clearImmersiveStickyMode(this); + } + + mArtboardList.clear(); + mArtboardPagerAdapter.notifyDataSetChanged(); + mArtboardListAdapter.notifyDataSetChanged(); + + mCurrentFlag = FLAG_MIRROR_LIST; + RxBus.getInstance().post(new ViewSwitchedEvent(FLAG_MIRROR_LIST)); + } + + private void switchToArtboardListView() { + if (mCurrentFlag == FLAG_MIRROR_LIST) { + AnimUtils.showViewAlphaAnim(mArtboardListView, mLinearInterpolator, true); + } else if (mCurrentFlag == FLAG_ARTBOARD_PAGER) { + TimeInterpolator interpolator; + if (mIsDragDismiss) { + interpolator = mAccelerateInterpolator; + } else { + interpolator = mLinearInterpolator; + } + AnimUtils.showViewAlphaAnim(mArtboardPagerView, interpolator, false); + AnimUtils.showViewAlphaAnim(mConnectStatusLayout, interpolator, true); + DisplayUtils.clearImmersiveStickyMode(this); + } + + mCurrentFlag = FLAG_ARTBOARD_LIST; + RxBus.getInstance().post(new ViewSwitchedEvent(FLAG_ARTBOARD_LIST)); + } + + private void switchToArtboardPagerView() { + if (mCurrentFlag != FLAG_ARTBOARD_LIST) { + return; + } + + AnimUtils.showViewAlphaAnim(mConnectStatusLayout, mLinearInterpolator, false); + AnimUtils.showViewAlphaAnim(mArtboardPagerView, mLinearInterpolator, true); + DisplayUtils.requestImmersiveStickyMode(this); + + mCurrentFlag = FLAG_ARTBOARD_PAGER; + RxBus.getInstance().post(new ViewSwitchedEvent(FLAG_ARTBOARD_PAGER)); + } + + // ============================================================================================= + + // when failed, always back to MirrorListView; + // and have any better code style? + private void setupRxBus() { + RxBus.getInstance().toObservable(WifiConnectingEvent.class) + .onBackpressureBuffer() + .compose(this.bindUntilEvent(ActivityEvent.DESTROY)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(WifiConnectingEvent event) { + onWifiConnectingEvent(); + } + }); + + RxBus.getInstance().toObservable(WifiConnectedEvent.class) + .onBackpressureBuffer() + .compose(this.bindUntilEvent(ActivityEvent.DESTROY)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(WifiConnectedEvent event) { + onWifiConnectedEvent(); + } + }); + + RxBus.getInstance().toObservable(WifiDisconnectedEvent.class) + .onBackpressureBuffer() + .compose(this.bindUntilEvent(ActivityEvent.DESTROY)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(WifiDisconnectedEvent event) { + onWifiDisconnectedEvent(); + } + }); + + RxBus.getInstance().toObservable(MirrorFoundEvent.class) + .onBackpressureBuffer() + .compose(this.bindUntilEvent(ActivityEvent.DESTROY)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(MirrorFoundEvent event) { + onMirrorFoundEvent(event); + } + }); + + RxBus.getInstance().toObservable(MirrorLostEvent.class) + .onBackpressureBuffer() + .compose(this.bindUntilEvent(ActivityEvent.DESTROY)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(MirrorLostEvent event) { + onMirrorLostEvent(event); + } + }); + + RxBus.getInstance().toObservable(MirrorResolveSuccessEvent.class) + .onBackpressureBuffer() + .compose(this.bindUntilEvent(ActivityEvent.DESTROY)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(MirrorResolveSuccessEvent event) { + onMirrorResolveSuccessEvent(event); + } + }); + + RxBus.getInstance().toObservable(MirrorResolveFailedEvent.class) + .onBackpressureBuffer() + .compose(this.bindUntilEvent(ActivityEvent.DESTROY)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(MirrorResolveFailedEvent event) { + onMirrorResolveFailedEvent(event); + } + }); + + RxBus.getInstance().toObservable(WebSocketCloseEvent.class) + .onBackpressureBuffer() + .compose(this.bindUntilEvent(ActivityEvent.DESTROY)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(WebSocketCloseEvent event) { + onWebSocketCloseEvent(event); + } + }); + + RxBus.getInstance().toObservable(WebSocketFailureEvent.class) + .onBackpressureBuffer() + .compose(this.bindUntilEvent(ActivityEvent.DESTROY)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(WebSocketFailureEvent event) { + onWebSocketFailureEvent(event); + } + }); + + RxBus.getInstance().toObservable(MirrorMessageEvent.class) + .onBackpressureBuffer() + .compose(this.bindUntilEvent(ActivityEvent.DESTROY)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(MirrorMessageEvent event) { + dispatchMirrorMessageEvent(event); + } + }); + + RxBus.getInstance().toObservable(ArtboardSelectedEvent.class) + .onBackpressureBuffer() + .compose(this.bindUntilEvent(ActivityEvent.DESTROY)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(ArtboardSelectedEvent event) { + onArtboardSelectedEvent(event); + } + }); + } + + private void onWifiConnectingEvent() { + mCurrentSSID = NetworkUtils.getCurrentSSID(this); + mConnectStatusLayout.setConnectStatus(ConnectStatusLayout.CONNECTING, mCurrentSSID); + } + + private void onWifiConnectedEvent() { + mCurrentSSID = NetworkUtils.getCurrentSSID(this); + mConnectStatusLayout.setConnectStatus(ConnectStatusLayout.CONNECTED, mCurrentSSID); + } + + private void onWifiDisconnectedEvent() { + mConnectStatusLayout.setConnectStatus(ConnectStatusLayout.DISCONNECTED, mCurrentSSID); + switchToMirrorListView(); + } + + // just name equals, have a better way? + private void onMirrorFoundEvent(MirrorFoundEvent event) { + MirrorInfo mirrorInfo = event.getMirrorInfo(); + int position = -1; + + for (int i = 0 ; i < mMirrorInfoList.size(); i++) { + MirrorInfo info = mMirrorInfoList.get(i); + if (TextUtils.equals(info.getName(), mirrorInfo.getName())) { + position = i; + break; + } + } + + // add 1 for top placeholder + if (position >= 0) { + mMirrorInfoList.set(position, mirrorInfo); + mMirrorListAdapter.notifyItemChanged(position + 1); + } else { + mMirrorInfoList.add(mirrorInfo); + mMirrorListAdapter.notifyItemInserted(mMirrorInfoList.size()); + } + } + + // just name equals, have a better way? + private void onMirrorLostEvent(MirrorLostEvent event) { + MirrorInfo mirrorInfo = event.getMirrorInfo(); + int position = -1; + + for (int i = 0 ; i < mMirrorInfoList.size(); i++) { + MirrorInfo info = mMirrorInfoList.get(i); + if (TextUtils.equals(info.getName(), mirrorInfo.getName())) { + position = i; + break; + } + } + + // add 1 for top placeholder + if (position >= 0) { + mMirrorInfoList.remove(position); + mMirrorListAdapter.notifyItemRemoved(position + 1); + } + + if (mCurrentMirror != null && TextUtils.equals(mCurrentMirror.getName(), mirrorInfo.getName())) { + String currentSketch = MirrorUtils.getCurrentSketch(this, mCurrentMirror); + mConnectStatusLayout.setConnectStatus(ConnectStatusLayout.DISCONNECTED, currentSketch); + switchToMirrorListView(); + } + } + + private void onMirrorResolveSuccessEvent(MirrorResolveSuccessEvent event) { + mCurrentMirror = event.getMirrorInfo(); + String currentSketch = MirrorUtils.getCurrentSketch(this, mCurrentMirror); + mConnectStatusLayout.setConnectStatus(ConnectStatusLayout.CONNECTING, currentSketch); + } + + private void onMirrorResolveFailedEvent(MirrorResolveFailedEvent event) { + mCurrentMirror = event.getMirrorInfo(); + String currentSketch = MirrorUtils.getCurrentSketch(this, mCurrentMirror); + mConnectStatusLayout.setConnectStatus(ConnectStatusLayout.DISCONNECTED, currentSketch); + switchToMirrorListView(); + } + + private void onWebSocketCloseEvent(WebSocketCloseEvent event) { + String currentSketch = MirrorUtils.getCurrentSketch(this, mCurrentMirror); + mConnectStatusLayout.setConnectStatus(ConnectStatusLayout.DISCONNECTED, currentSketch); + switchToMirrorListView(); + } + + private void onWebSocketFailureEvent(WebSocketFailureEvent event) { + String currentSketch = MirrorUtils.getCurrentSketch(this, mCurrentMirror); + mConnectStatusLayout.setConnectStatus(ConnectStatusLayout.DISCONNECTED, currentSketch); + switchToMirrorListView(); + } + + private void dispatchMirrorMessageEvent(MirrorMessageEvent event) { + Message message = event.getMessage(); + if (TextUtils.equals(message.getType(), MirrorUtils.TYPE_CONNECTED)) { + onMirrorMessageConnected(); + } else if (TextUtils.equals(message.getType(), MirrorUtils.TYPE_DISCONNECTED)) { + onMirrorMessageDisconnected(); + } else if (TextUtils.equals(message.getType(), MirrorUtils.TYPE_MANIFEST)) { + onMirrorMessageManifest(message); + } else if (TextUtils.equals(message.getType(), MirrorUtils.TYPE_ARTBOARD)) { + onMirrorMessageArtboard(message); + } else if (TextUtils.equals(message.getType(), MirrorUtils.TYPE_CURRENT_ARTBOARD)) { + onMirrorMessageCurrentArtboard(message); + } + } + + private void onMirrorMessageConnected() { + String currentSketch = MirrorUtils.getCurrentSketch(this, mCurrentMirror); + mConnectStatusLayout.setConnectStatus(ConnectStatusLayout.CONNECTED, currentSketch); + switchToArtboardListView(); + } + + private void onMirrorMessageDisconnected() { + String currentSketch = MirrorUtils.getCurrentSketch(this, mCurrentMirror); + mConnectStatusLayout.setConnectStatus(ConnectStatusLayout.DISCONNECTED, currentSketch); + switchToMirrorListView(); + } + + private void onMirrorMessageManifest(Message message) { + Content content = message.getContent(); + + // when id difference, means Sketch file changed + if (TextUtils.isEmpty(mCurrentManifestId)) { + mCurrentManifestId = content.getId(); + } else { + if (!TextUtils.equals(content.getId(), mCurrentManifestId)) { + mCurrentManifestId = content.getId(); + switchToArtboardListView(); + } + } + + final List oldList = new LinkedList<>(mArtboardList); + mArtboardList.clear(); + mArtboardList.addAll(MirrorUtils.getArtboardsFromContent(this, content)); + DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() { + @Override + public int getOldListSize() { + return oldList.size(); + } + + @Override + public int getNewListSize() { + return mArtboardList.size(); + } + + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + Artboard old = oldList.get(oldItemPosition); + Artboard nez = mArtboardList.get(newItemPosition); + return TextUtils.equals(old.getId(), nez.getId()); + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + Artboard old = oldList.get(oldItemPosition); + Artboard nez = mArtboardList.get(newItemPosition); + nez.setNeedUpdateInPager(old.isNeedUpdateInPager()); // when pager updated then false + return !old.isNeedUpdateInList() && old.equals(nez); + } + }); + + // add 1 for top placeholder + result.dispatchUpdatesTo(new ListUpdateCallback() { + @Override + public void onInserted(int position, int count) { + mArtboardListAdapter.notifyItemRangeInserted(position + 1, count); + } + + @Override + public void onRemoved(int position, int count) { + mArtboardListAdapter.notifyItemRangeRemoved(position + 1, count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + mArtboardListAdapter.notifyItemMoved(fromPosition + 1, toPosition + 1); + } + + @Override + public void onChanged(int position, int count, Object payload) { + mArtboardListAdapter.notifyItemRangeChanged(position + 1, count, payload); + } + }); + + mArtboardPagerAdapter.notifyDataSetChanged(); + } + + // also update when next TYPE_MANIFEST arrive + private void onMirrorMessageArtboard(Message message) { + Content content = message.getContent(); + int position = -1; + + for (int i = 0; i < mArtboardList.size(); i++) { + Artboard artboard = mArtboardList.get(i); + if (TextUtils.equals(artboard.getId(), content.getIdentifier())) { + artboard.setNeedUpdateInList(true); + artboard.setNeedUpdateInPager(true); + position = i; + break; + } + } + + // add 1 for top placeholder + if (position >= 0) { + mArtboardListAdapter.notifyItemChanged(position + 1); + mArtboardPagerAdapter.notifyDataSetChanged(); + mArtboardPagerView.setCurrentItem(position, false); + } + } + + // not much use + private void onMirrorMessageCurrentArtboard(Message message) { + String id = message.getContent().getIdentifier(); + int position = -1; + + for (int i = 0; i < mArtboardList.size(); i++) { + Artboard artboard = mArtboardList.get(i); + if (TextUtils.equals(artboard.getId(), id)) { + position = i; + break; + } + } + + if (position >= 0) { + mArtboardPagerView.setCurrentItem(position, false); + } + } + + private void onArtboardSelectedEvent(ArtboardSelectedEvent event) { + Artboard artboard = event.getArtboard(); + int position = mArtboardList.indexOf(artboard); + if (position < 0) { + return; + } + + mArtboardPagerView.setCurrentItem(position, false); + switchToArtboardPagerView(); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/app/MirrorService.java b/app/src/main/java/com/zhihu/android/app/mirror/app/MirrorService.java new file mode 100644 index 0000000..a5d0ddb --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/app/MirrorService.java @@ -0,0 +1,392 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.app; + +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.nsd.NsdManager; +import android.net.nsd.NsdServiceInfo; +import android.os.IBinder; +import android.text.TextUtils; + +import com.google.gson.Gson; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import com.zhihu.android.app.mirror.event.MirrorFoundEvent; +import com.zhihu.android.app.mirror.event.MirrorLostEvent; +import com.zhihu.android.app.mirror.event.MirrorMessageEvent; +import com.zhihu.android.app.mirror.event.MirrorResolveFailedEvent; +import com.zhihu.android.app.mirror.event.MirrorResolveSuccessEvent; +import com.zhihu.android.app.mirror.event.MirrorSelectedEvent; +import com.zhihu.android.app.mirror.event.ViewSwitchedEvent; +import com.zhihu.android.app.mirror.event.WebSocketCloseEvent; +import com.zhihu.android.app.mirror.event.WebSocketFailureEvent; +import com.zhihu.android.app.mirror.event.WifiConnectedEvent; +import com.zhihu.android.app.mirror.event.WifiConnectingEvent; +import com.zhihu.android.app.mirror.event.WifiDisconnectedEvent; +import com.zhihu.android.app.mirror.model.Message; +import com.zhihu.android.app.mirror.model.MirrorInfo; +import com.zhihu.android.app.mirror.util.NetworkUtils; +import com.zhihu.android.app.mirror.util.MirrorUtils; +import com.zhihu.android.app.mirror.util.PreferenceUtils; +import com.zhihu.android.app.mirror.util.RxBus; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.ws.WebSocket; +import okhttp3.ws.WebSocketCall; +import okhttp3.ws.WebSocketListener; +import okio.Buffer; +import rx.Subscriber; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.subscriptions.CompositeSubscription; + +public class MirrorService extends Service implements WebSocketListener { + private static final String SERVICE_TYPE = MirrorUtils.MIRROR_TYPE; + private static final int PROTOCOL_TYPE = NsdManager.PROTOCOL_DNS_SD; + + private BroadcastReceiver mWifiReceiver; + private boolean mIsWifiConnected; + private boolean mIsWifiDisconnected; + + private NsdManager mNsdManager; + private NsdManager.DiscoveryListener mDiscoveryListener; + + private WebSocketCall mWebSocketCall; + private Gson mGson; + + private CompositeSubscription mCompositeSubscription; + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + setupWifiConnect(); + setupRxBus(); + return START_STICKY; + } + + @Override + public void onDestroy() { + unregisterReceiverSafety(); + closeDiscoveryListenerSafety(); + closeWebSocketSafety(); + + if (mCompositeSubscription != null) { + mCompositeSubscription.unsubscribe(); + } + } + + private void unregisterReceiverSafety() { + try { + if (mWifiReceiver != null) { + unregisterReceiver(mWifiReceiver); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void closeDiscoveryListenerSafety() { + try { + if (mNsdManager != null && mDiscoveryListener != null) { + mNsdManager.stopServiceDiscovery(mDiscoveryListener); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void closeWebSocketSafety() { + try { + if (mWebSocketCall != null) { + mWebSocketCall.cancel(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // ============================================================================================= + + private void setupWifiConnect() { + unregisterReceiverSafety(); + closeDiscoveryListenerSafety(); + closeWebSocketSafety(); + + mWifiReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!TextUtils.equals(ConnectivityManager.CONNECTIVITY_ACTION, intent.getAction())) { + return; + } + + ConnectivityManager manager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); + NetworkInfo info = manager.getActiveNetworkInfo(); + if (info == null) { + onWifiDisconnected(); + return; + } + + if (info.getType() != ConnectivityManager.TYPE_WIFI) { + onWifiDisconnected(); + return; + } + + if (!info.isConnected()) { + onWifiConnecting(); + } else { + onWifiConnected(); + } + } + }; + + IntentFilter filter = new IntentFilter(); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + registerReceiver(mWifiReceiver, filter); + + if (!NetworkUtils.isWifiConnected(this)) { + NetworkUtils.setWifiEnabled(this, true); + } + } + + private void onWifiConnecting() { + RxBus.getInstance().post(new WifiConnectingEvent()); + } + + private void onWifiConnected() { + if (mIsWifiConnected) { + return; + } + mIsWifiConnected = true; + mIsWifiDisconnected = false; + + closeDiscoveryListenerSafety(); + RxBus.getInstance().post(new WifiConnectedEvent()); + + mNsdManager = (NsdManager) getSystemService(NSD_SERVICE); + mDiscoveryListener = buildDiscoveryListener(); + mNsdManager.discoverServices(SERVICE_TYPE, PROTOCOL_TYPE, mDiscoveryListener); + } + + private NsdManager.DiscoveryListener buildDiscoveryListener() { + return new NsdManager.DiscoveryListener() { + @Override + public void onStartDiscoveryFailed(String serviceType, int errorCode) { + // DO NOTHING + } + + @Override + public void onStopDiscoveryFailed(String serviceType, int errorCode) { + // DO NOTHING + } + + @Override + public void onDiscoveryStarted(String serviceType) { + // DO NOTHING + } + + @Override + public void onDiscoveryStopped(String serviceType) { + // DO NOTHING + } + + @Override + public void onServiceFound(NsdServiceInfo nsdServiceInfo) { + if (nsdServiceInfo.getServiceName().startsWith(MirrorUtils.MIRROR_FILTER)) { + RxBus.getInstance().post(new MirrorFoundEvent(new MirrorInfo(nsdServiceInfo))); + } + } + + @Override + public void onServiceLost(NsdServiceInfo nsdServiceInfo) { + if (nsdServiceInfo.getServiceName().startsWith(MirrorUtils.MIRROR_FILTER)) { + RxBus.getInstance().post(new MirrorLostEvent(new MirrorInfo(nsdServiceInfo))); + } + } + }; + } + + private void onWifiDisconnected() { + if (mIsWifiDisconnected) { + return; + } + mIsWifiConnected = false; + mIsWifiDisconnected = true; + + closeDiscoveryListenerSafety(); + closeWebSocketSafety(); + RxBus.getInstance().post(new WifiDisconnectedEvent()); + } + + // ============================================================================================= + + private void setupRxBus() { + if (mCompositeSubscription != null) { + mCompositeSubscription.unsubscribe(); + } + mCompositeSubscription = new CompositeSubscription(); + + Subscription subscription = RxBus.getInstance().toObservable(MirrorSelectedEvent.class) + .onBackpressureBuffer() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(MirrorSelectedEvent event) { + onMirrorSelectedEvent(event); + } + }); + mCompositeSubscription.add(subscription); + + subscription = RxBus.getInstance().toObservable(ViewSwitchedEvent.class) + .onBackpressureBuffer() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(ViewSwitchedEvent event) { + onViewSwitchedEvent(event); + } + }); + mCompositeSubscription.add(subscription); + } + + private void onMirrorSelectedEvent(MirrorSelectedEvent event) { + closeWebSocketSafety(); + mNsdManager.resolveService(event.getMirrorInfo().getNsdServiceInfo(), + buildResolveListener()); + } + + private NsdManager.ResolveListener buildResolveListener() { + return new NsdManager.ResolveListener() { + @Override + public void onResolveFailed(NsdServiceInfo nsdServiceInfo, int errorCode) { + MirrorService.this.onResolveFailed(nsdServiceInfo); + } + + @Override + public void onServiceResolved(NsdServiceInfo nsdServiceInfo) { + onResolveSuccess(nsdServiceInfo); + } + }; + } + + private void onResolveFailed(NsdServiceInfo nsdServiceInfo) { + RxBus.getInstance().post(new MirrorResolveFailedEvent(new MirrorInfo(nsdServiceInfo))); + } + + private void onResolveSuccess(NsdServiceInfo nsdServiceInfo) { + MirrorInfo mirrorInfo = new MirrorInfo(nsdServiceInfo); + PreferenceUtils.setMirror(this, MirrorUtils.buildMirrorHttpUrl(mirrorInfo)); + RxBus.getInstance().post(new MirrorResolveSuccessEvent(mirrorInfo)); + + OkHttpClient client = new OkHttpClient.Builder() + .connectTimeout(MirrorUtils.MIRROR_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS) + .readTimeout(MirrorUtils.MIRROR_READ_TIMEOUT, TimeUnit.MILLISECONDS) + .retryOnConnectionFailure(true) + .build(); + Request request = new Request.Builder() + .url(MirrorUtils.buildMirrorWebSocketUrl(mirrorInfo)) + .build(); + mWebSocketCall = WebSocketCall.create(client, request); + mWebSocketCall.enqueue(this); + } + + private void onViewSwitchedEvent(ViewSwitchedEvent event) { + if (event.getCurrentType() == ViewSwitchedEvent.FLAG_MIRROR_LIST) { + closeWebSocketSafety(); + } + } + + // ============================================================================================= + + @Override + public void onOpen(WebSocket webSocket, Response response) { + mGson = new Gson(); + String json = mGson.toJson(MirrorUtils.buildHandshake(this)); + + try { + webSocket.sendMessage(RequestBody.create(WebSocket.TEXT, json)); + } catch (IOException e) { + onFailure(e, null); + } + } + + @Override + public void onFailure(IOException e, Response response) { + e.printStackTrace(); // sometimes EOF, how to fix? + RxBus.getInstance().post(new WebSocketFailureEvent(e, response)); + } + + @Override + public void onMessage(ResponseBody body) throws IOException { + Message message = mGson.fromJson(body.string(), Message.class); + + // not TYPE_DISCONNECTED + if (TextUtils.equals(MirrorUtils.TYPE_CONNECTED, message.getType())) { + PreferenceUtils.setToken(this, message.getContent().getToken()); + PreferenceUtils.setDevice(this, message.getContent().getDevice()); + } + + RxBus.getInstance().post(new MirrorMessageEvent(message)); + body.close(); + } + + @Override + public void onPong(Buffer payload) { + // DO NOTHING + } + + @Override + public void onClose(int code, String reason) { + RxBus.getInstance().post(new WebSocketCloseEvent(code, reason)); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/ArtboardSelectedEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/ArtboardSelectedEvent.java new file mode 100644 index 0000000..032fd11 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/ArtboardSelectedEvent.java @@ -0,0 +1,37 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +import com.zhihu.android.app.mirror.model.Artboard; + +public class ArtboardSelectedEvent { + private Artboard mArtboard; + + public ArtboardSelectedEvent(Artboard artboard) { + mArtboard = artboard; + } + + public Artboard getArtboard() { + return mArtboard; + } + + public void setArtboard(Artboard artboard) { + mArtboard = artboard; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorFoundEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorFoundEvent.java new file mode 100644 index 0000000..95e78a4 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorFoundEvent.java @@ -0,0 +1,37 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +import com.zhihu.android.app.mirror.model.MirrorInfo; + +public class MirrorFoundEvent { + private MirrorInfo mMirrorInfo; + + public MirrorFoundEvent(MirrorInfo mirrorInfo) { + mMirrorInfo = mirrorInfo; + } + + public MirrorInfo getMirrorInfo() { + return mMirrorInfo; + } + + public void setMirrorInfo(MirrorInfo mirrorInfo) { + mMirrorInfo = mirrorInfo; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorLostEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorLostEvent.java new file mode 100644 index 0000000..2ead25e --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorLostEvent.java @@ -0,0 +1,37 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +import com.zhihu.android.app.mirror.model.MirrorInfo; + +public class MirrorLostEvent { + private MirrorInfo mMirrorInfo; + + public MirrorLostEvent(MirrorInfo mirrorInfo) { + mMirrorInfo = mirrorInfo; + } + + public MirrorInfo getMirrorInfo() { + return mMirrorInfo; + } + + public void setMirrorInfo(MirrorInfo mirrorInfo) { + mMirrorInfo = mirrorInfo; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorMessageEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorMessageEvent.java new file mode 100644 index 0000000..f54612d --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorMessageEvent.java @@ -0,0 +1,37 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +import com.zhihu.android.app.mirror.model.Message; + +public class MirrorMessageEvent { + private Message mMessage; + + public MirrorMessageEvent(Message message) { + mMessage = message; + } + + public Message getMessage() { + return mMessage; + } + + public void setMessage(Message message) { + mMessage = message; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorResolveFailedEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorResolveFailedEvent.java new file mode 100644 index 0000000..6267b3c --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorResolveFailedEvent.java @@ -0,0 +1,37 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +import com.zhihu.android.app.mirror.model.MirrorInfo; + +public class MirrorResolveFailedEvent { + private MirrorInfo mMirrorInfo; + + public MirrorResolveFailedEvent(MirrorInfo mirrorInfo) { + mMirrorInfo = mirrorInfo; + } + + public MirrorInfo getMirrorInfo() { + return mMirrorInfo; + } + + public void setMirrorInfo(MirrorInfo mirrorInfo) { + mMirrorInfo = mirrorInfo; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorResolveSuccessEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorResolveSuccessEvent.java new file mode 100644 index 0000000..d5b46c7 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorResolveSuccessEvent.java @@ -0,0 +1,37 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +import com.zhihu.android.app.mirror.model.MirrorInfo; + +public class MirrorResolveSuccessEvent { + private MirrorInfo mMirrorInfo; + + public MirrorResolveSuccessEvent(MirrorInfo mirrorInfo) { + mMirrorInfo = mirrorInfo; + } + + public MirrorInfo getMirrorInfo() { + return mMirrorInfo; + } + + public void setMirrorInfo(MirrorInfo mirrorInfo) { + mMirrorInfo = mirrorInfo; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorSelectedEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorSelectedEvent.java new file mode 100644 index 0000000..8eaf808 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/MirrorSelectedEvent.java @@ -0,0 +1,37 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +import com.zhihu.android.app.mirror.model.MirrorInfo; + +public class MirrorSelectedEvent { + private MirrorInfo mMirrorInfo; + + public MirrorSelectedEvent(MirrorInfo mirrorInfo) { + mMirrorInfo = mirrorInfo; + } + + public MirrorInfo getMirrorInfo() { + return mMirrorInfo; + } + + public void setMirrorInfo(MirrorInfo mirrorInfo) { + mMirrorInfo = mirrorInfo; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/ViewSwitchedEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/ViewSwitchedEvent.java new file mode 100644 index 0000000..d13843c --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/ViewSwitchedEvent.java @@ -0,0 +1,52 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +import android.support.annotation.IntDef; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public class ViewSwitchedEvent { + public static final int FLAG_MIRROR_LIST = 0x00; + public static final int FLAG_ARTBOARD_LIST = 0x01; + public static final int FLAG_ARTBOARD_PAGER = 0x02; + + @IntDef({FLAG_MIRROR_LIST, FLAG_ARTBOARD_LIST, FLAG_ARTBOARD_PAGER}) + @Retention(RetentionPolicy.SOURCE) + @Target({ElementType.FIELD, ElementType.PARAMETER}) + public @interface BackPressedType {} + + @BackPressedType + private int mCurrentType; + + public ViewSwitchedEvent(@BackPressedType int currentType) { + mCurrentType = currentType; + } + + public int getCurrentType() { + return mCurrentType; + } + + public void setCurrentType(int currentType) { + mCurrentType = currentType; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/WebSocketCloseEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/WebSocketCloseEvent.java new file mode 100644 index 0000000..1ef11de --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/WebSocketCloseEvent.java @@ -0,0 +1,45 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +public class WebSocketCloseEvent { + private int mCode; + private String mReason; + + public WebSocketCloseEvent(int code, String reason) { + mCode = code; + mReason = reason; + } + + public int getCode() { + return mCode; + } + + public void setCode(int code) { + mCode = code; + } + + public String getReason() { + return mReason; + } + + public void setReason(String reason) { + mReason = reason; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/WebSocketFailureEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/WebSocketFailureEvent.java new file mode 100644 index 0000000..0647528 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/WebSocketFailureEvent.java @@ -0,0 +1,49 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +import java.io.IOException; + +import okhttp3.Response; + +public class WebSocketFailureEvent { + private IOException mIOException; + private Response mResponse; + + public WebSocketFailureEvent(IOException ioException, Response response) { + mIOException = ioException; + mResponse = response; + } + + public IOException getIOException() { + return mIOException; + } + + public void setIOException(IOException IOException) { + mIOException = IOException; + } + + public Response getResponse() { + return mResponse; + } + + public void setResponse(Response response) { + mResponse = response; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/WifiConnectedEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/WifiConnectedEvent.java new file mode 100644 index 0000000..b7ec4bf --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/WifiConnectedEvent.java @@ -0,0 +1,21 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +public class WifiConnectedEvent {} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/WifiConnectingEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/WifiConnectingEvent.java new file mode 100644 index 0000000..99e8249 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/WifiConnectingEvent.java @@ -0,0 +1,21 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +public class WifiConnectingEvent {} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/event/WifiDisconnectedEvent.java b/app/src/main/java/com/zhihu/android/app/mirror/event/WifiDisconnectedEvent.java new file mode 100644 index 0000000..0ac6df5 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/event/WifiDisconnectedEvent.java @@ -0,0 +1,21 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.event; + +public class WifiDisconnectedEvent {} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/model/Artboard.java b/app/src/main/java/com/zhihu/android/app/mirror/model/Artboard.java new file mode 100644 index 0000000..5ac1936 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/model/Artboard.java @@ -0,0 +1,133 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.model; + +import android.text.TextUtils; + +import com.google.gson.annotations.SerializedName; + +public class Artboard { + @SerializedName("id") + private String mId; + + @SerializedName("slug") + private String mSlug; + + // @SerializedName("files") + // private File[] mFiles; + + @SerializedName("name") + private String mName; + + @SerializedName("width") + private int mWidth; + + @SerializedName("height") + private int mHeight; + + @SerializedName("path") + private String mPath; + + // force to update + private boolean mNeedUpdateInList; + + private boolean mNeedUpdateInPager; + + public String getId() { + return mId; + } + + public void setId(String id) { + mId = id; + } + + public String getSlug() { + return mSlug; + } + + public void setSlug(String slug) { + mSlug = slug; + } + + public String getName() { + return mName; + } + + public void setName(String name) { + mName = name; + } + + public int getWidth() { + return mWidth; + } + + public void setWidth(int width) { + mWidth = width; + } + + public int getHeight() { + return mHeight; + } + + public void setHeight(int height) { + mHeight = height; + } + + public String getPath() { + return mPath; + } + + public void setPath(String path) { + mPath = path; + } + + public boolean isNeedUpdateInList() { + return mNeedUpdateInList; + } + + public void setNeedUpdateInList(boolean needUpdateInList) { + mNeedUpdateInList = needUpdateInList; + } + + public boolean isNeedUpdateInPager() { + return mNeedUpdateInPager; + } + + public void setNeedUpdateInPager(boolean needUpdateInPager) { + mNeedUpdateInPager = needUpdateInPager; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof Artboard)) { + return false; + } + + if (this == object) { + return true; + } + + Artboard artboard = (Artboard) object; + return TextUtils.equals(mId, artboard.mId) + && TextUtils.equals(mSlug, artboard.getSlug()) + && TextUtils.equals(mName, artboard.getName()) + && mWidth == artboard.getWidth() + && mHeight == artboard.getHeight(); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/model/Content.java b/app/src/main/java/com/zhihu/android/app/mirror/model/Content.java new file mode 100644 index 0000000..23f9a8f --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/model/Content.java @@ -0,0 +1,177 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.model; + +import com.google.gson.annotations.SerializedName; + +public class Content { + @SerializedName("type") + private String mType; + + @SerializedName("display-name") + private String mDisplayName; + + @SerializedName("screens") + private Screen[] mScreens; + + @SerializedName("user-agent") + private String mUserAgent; + + @SerializedName("uuid") + private String mUuid; + + @SerializedName("token") + private String mToken; + + @SerializedName("device") + private String mDevice; + + @SerializedName("contents") + private Contents mContents; + + @SerializedName("id") + private String mId; + + @SerializedName("metadata") + private Metadata mMetadata; + + @SerializedName("name") + private String mName; + + @SerializedName("url") + private String mUrl; + + @SerializedName("identifier") + private String mIdentifier; + + @SerializedName("path") + private String mPath; + + public String getType() { + return mType; + } + + public void setType(String type) { + mType = type; + } + + public String getDisplayName() { + return mDisplayName; + } + + public void setDisplayName(String displayName) { + mDisplayName = displayName; + } + + public Screen[] getScreens() { + return mScreens; + } + + public void setScreens(Screen[] screens) { + mScreens = screens; + } + + public String getUserAgent() { + return mUserAgent; + } + + public void setUserAgent(String userAgent) { + mUserAgent = userAgent; + } + + public String getUuid() { + return mUuid; + } + + public void setUuid(String uuid) { + mUuid = uuid; + } + + public String getToken() { + return mToken; + } + + public void setToken(String token) { + mToken = token; + } + + public String getDevice() { + return mDevice; + } + + public void setDevice(String device) { + mDevice = device; + } + + public Contents getContents() { + return mContents; + } + + public void setContents(Contents contents) { + mContents = contents; + } + + public String getId() { + return mId; + } + + public void setId(String id) { + mId = id; + } + + public Metadata getMetadata() { + return mMetadata; + } + + public void setMetadata(Metadata metadata) { + mMetadata = metadata; + } + + public String getName() { + return mName; + } + + public void setName(String name) { + mName = name; + } + + public String getUrl() { + return mUrl; + } + + public void setUrl(String url) { + mUrl = url; + } + + public String getIdentifier() { + return mIdentifier; + } + + public void setIdentifier(String identifier) { + mIdentifier = identifier; + } + + public String getPath() { + return mPath; + } + + public void setPath(String path) { + mPath = path; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/model/Contents.java b/app/src/main/java/com/zhihu/android/app/mirror/model/Contents.java new file mode 100644 index 0000000..66770c8 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/model/Contents.java @@ -0,0 +1,34 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.model; + +import com.google.gson.annotations.SerializedName; + +public class Contents { + @SerializedName("pages") + private Page[] mPages; + + public Page[] getPages() { + return mPages; + } + + public void setPages(Page[] pages) { + mPages = pages; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/model/Handshake.java b/app/src/main/java/com/zhihu/android/app/mirror/model/Handshake.java new file mode 100644 index 0000000..d388e8d --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/model/Handshake.java @@ -0,0 +1,56 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.model; + +import com.google.gson.annotations.SerializedName; + +public class Handshake { + @SerializedName("type") + private String mType; + + @SerializedName("instance-id") + private String mInstanceId; + + @SerializedName("content") + private Content mContent; + + public String getType() { + return mType; + } + + public void setType(String type) { + mType = type; + } + + public String getInstanceId() { + return mInstanceId; + } + + public void setInstanceId(String instanceId) { + mInstanceId = instanceId; + } + + public Content getContent() { + return mContent; + } + + public void setContent(Content content) { + mContent = content; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/model/Message.java b/app/src/main/java/com/zhihu/android/app/mirror/model/Message.java new file mode 100644 index 0000000..e048319 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/model/Message.java @@ -0,0 +1,45 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.model; + +import com.google.gson.annotations.SerializedName; + +public class Message { + @SerializedName("type") + private String mType; + + @SerializedName("content") + private Content mContent; + + public String getType() { + return mType; + } + + public void setType(String type) { + mType = type; + } + + public Content getContent() { + return mContent; + } + + public void setContent(Content content) { + mContent = content; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/model/Metadata.java b/app/src/main/java/com/zhihu/android/app/mirror/model/Metadata.java new file mode 100644 index 0000000..cdbf362 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/model/Metadata.java @@ -0,0 +1,45 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.model; + +import com.google.gson.annotations.SerializedName; + +public class Metadata { + @SerializedName("version") + private int mVersion; + + @SerializedName("updated") + private long mUpdated; + + public int getVersion() { + return mVersion; + } + + public void setVersion(int version) { + mVersion = version; + } + + public long getUpdated() { + return mUpdated; + } + + public void setUpdated(long updated) { + mUpdated = updated; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/model/MirrorInfo.java b/app/src/main/java/com/zhihu/android/app/mirror/model/MirrorInfo.java new file mode 100644 index 0000000..7548944 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/model/MirrorInfo.java @@ -0,0 +1,96 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.model; + +import android.net.nsd.NsdServiceInfo; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MirrorInfo { + private String mName; + private String mHost; + private int mPort; + private NsdServiceInfo mNsdServiceInfo; + + public MirrorInfo(NsdServiceInfo nsdServiceInfo) { + Pattern pattern = Pattern.compile("\\((.*?)\\)"); + Matcher matcher = pattern.matcher(nsdServiceInfo.getServiceName()); + + mName = ""; + while (matcher.find()) { + mName = matcher.group(1); + } + + mHost = ""; + if (nsdServiceInfo.getHost() != null) { + mHost = nsdServiceInfo.getHost().getHostAddress(); + } + + mPort = nsdServiceInfo.getPort(); + mNsdServiceInfo = nsdServiceInfo; + } + + public String getName() { + return mName; + } + + public void setName(String name) { + mName = name; + } + + public String getHost() { + return mHost; + } + + public void setHost(String host) { + mHost = host; + } + + public int getPort() { + return mPort; + } + + public void setPort(int port) { + mPort = port; + } + + public NsdServiceInfo getNsdServiceInfo() { + return mNsdServiceInfo; + } + + public void setNsdServiceInfo(NsdServiceInfo nsdServiceInfo) { + mNsdServiceInfo = nsdServiceInfo; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof MirrorInfo)) { + return false; + } + + if (this == object) { + return true; + } + + MirrorInfo info = (MirrorInfo) object; + // return TextUtils.equals(mName, info.getName()) && TextUtils.equals(mHost, info.getHost()) && mPort == info.getPort(); + return mNsdServiceInfo.equals(info.getNsdServiceInfo()); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/model/Page.java b/app/src/main/java/com/zhihu/android/app/mirror/model/Page.java new file mode 100644 index 0000000..0e4cd83 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/model/Page.java @@ -0,0 +1,67 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.model; + +import com.google.gson.annotations.SerializedName; + +public class Page { + @SerializedName("id") + private String mId; + + @SerializedName("slug") + private String mSlug; + + @SerializedName("name") + private String mName; + + @SerializedName("artboards") + private Artboard[] mArtboards; + + public String getId() { + return mId; + } + + public void setId(String id) { + mId = id; + } + + public String getSlug() { + return mSlug; + } + + public void setSlug(String slug) { + mSlug = slug; + } + + public String getName() { + return mName; + } + + public void setName(String name) { + mName = name; + } + + public Artboard[] getArtboards() { + return mArtboards; + } + + public void setArtboards(Artboard[] artboards) { + mArtboards = artboards; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/model/Screen.java b/app/src/main/java/com/zhihu/android/app/mirror/model/Screen.java new file mode 100644 index 0000000..9d3f586 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/model/Screen.java @@ -0,0 +1,67 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.model; + +import com.google.gson.annotations.SerializedName; + +public class Screen { + @SerializedName("name") + private String mName; + + @SerializedName("scale") + private float mScale; + + @SerializedName("width") + private int mWidth; + + @SerializedName("height") + private int mHeight; + + public String getName() { + return mName; + } + + public void setName(String name) { + mName = name; + } + + public float getScale() { + return mScale; + } + + public void setScale(float scale) { + mScale = scale; + } + + public int getWidth() { + return mWidth; + } + + public void setWidth(int width) { + mWidth = width; + } + + public int getHeight() { + return mHeight; + } + + public void setHeight(int height) { + mHeight = height; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/util/AnimUtils.java b/app/src/main/java/com/zhihu/android/app/mirror/util/AnimUtils.java new file mode 100644 index 0000000..a0b314b --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/util/AnimUtils.java @@ -0,0 +1,56 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.util; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.TimeInterpolator; +import android.view.View; + +public class AnimUtils { + public static final int DURATION = 300; // ms + + public static void showViewAlphaAnim(final View view, TimeInterpolator interpolator, final boolean visible) { + if (visible && view.getVisibility() == View.VISIBLE + || !visible && view.getVisibility() != View.VISIBLE) { + return; + } + + float fromAlpha = visible ? 0.0F : 1.0F; + float toAlpha = visible ? 1.0F : 0.0F; + view.setAlpha(fromAlpha); + view.setVisibility(View.VISIBLE); + view.animate().alpha(toAlpha) + .setDuration(DURATION) + .setInterpolator(interpolator) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + if (visible) { + view.setAlpha(1.0F); + view.setVisibility(View.VISIBLE); + } else { + view.setAlpha(0.0F); + view.setVisibility(View.INVISIBLE); + } + } + }) + .start(); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/util/DisplayUtils.java b/app/src/main/java/com/zhihu/android/app/mirror/util/DisplayUtils.java new file mode 100644 index 0000000..fbc71ab --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/util/DisplayUtils.java @@ -0,0 +1,91 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.util; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Point; +import android.view.Display; +import android.view.View; +import android.view.WindowManager; + +public class DisplayUtils { + public static float getScreenDensity(Context context) { + return context.getResources().getDisplayMetrics().density; + } + + public static int getScreenWidth(Context context) { + WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + Display display = manager.getDefaultDisplay(); + Point point = new Point(); + display.getSize(point); + return point.x; + } + + public static int getScreenHeight(Context context) { + WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + Display display = manager.getDefaultDisplay(); + Point point = new Point(); + display.getSize(point); + return point.y; + } + + public static int getStatusBarHeight(Context context) { + int statusBarHeight = 0; + + int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = context.getResources().getDimensionPixelSize(resourceId); + } + + return statusBarHeight; + } + + public static int getNavigationBarHeight(Context context) { + int navigationBarHeight = 0; + + int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android"); + if (resourceId > 0) { + navigationBarHeight = context.getResources().getDimensionPixelSize(resourceId); + } + + return navigationBarHeight; + } + + public static int dp2px(Context context, float dp) { + return (int) (getScreenDensity(context) * dp + 0.5F); + } + + public static int sp2px(Context context, float sp) { + return (int) (context.getResources().getDisplayMetrics().scaledDensity * sp + 0.5F); + } + + public static void requestImmersiveStickyMode(Activity activity) { + activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } + + public static void clearImmersiveStickyMode(Activity activity) { + activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/util/MirrorUtils.java b/app/src/main/java/com/zhihu/android/app/mirror/util/MirrorUtils.java new file mode 100644 index 0000000..b5333f3 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/util/MirrorUtils.java @@ -0,0 +1,124 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.util; + +import android.content.Context; +import android.os.Build; +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import com.zhihu.android.app.mirror.R; +import com.zhihu.android.app.mirror.model.Artboard; +import com.zhihu.android.app.mirror.model.Content; +import com.zhihu.android.app.mirror.model.Handshake; +import com.zhihu.android.app.mirror.model.MirrorInfo; +import com.zhihu.android.app.mirror.model.Page; +import com.zhihu.android.app.mirror.model.Screen; + +public class MirrorUtils { + public static final String MIRROR_FILTER = "Sketch Mirror"; + public static final String MIRROR_TYPE = "_http._tcp."; + public static final int MIRROR_CONNECT_TIMEOUT = 60000; // ms + public static final int MIRROR_READ_TIMEOUT = Integer.MAX_VALUE; // ms + + public static final String TYPE_DEVICE_INFO = "device-info"; + public static final String TYPE_CONNECTED = "connected"; + public static final String TYPE_MANIFEST = "manifest"; + public static final String TYPE_CURRENT_ARTBOARD = "current-artboard"; + public static final String TYPE_ARTBOARD = "artboard"; + public static final String TYPE_DISCONNECTED = "disconnected"; + + private static final String BUILD_HANDSHAKE_TYPE = TYPE_DEVICE_INFO; + private static final String BUILD_HANDSHAKE_CONTENT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36"; + private static final String BUILD_HANDSHAKE_CONTENT_DISPLAY_NAME = Build.MODEL; + private static final String BUILD_HANDSHAKE_SCREEN_NAME = Build.MODEL; + + public static String buildMirrorHttpUrl(MirrorInfo mirrorInfo) { + return "http://" + mirrorInfo.getHost() + ":" + mirrorInfo.getPort(); + } + + public static String buildMirrorWebSocketUrl(MirrorInfo mirrorInfo) { + return "ws://" + mirrorInfo.getHost() + ":" + (mirrorInfo.getPort() + 1); + } + + public static Handshake buildHandshake(Context context) { + Screen screen = new Screen(); + screen.setName(BUILD_HANDSHAKE_SCREEN_NAME); + screen.setWidth(DisplayUtils.getScreenWidth(context)); + screen.setHeight(DisplayUtils.getScreenHeight(context)); + screen.setScale(DisplayUtils.getScreenDensity(context)); // Math.round() + + String contentUuid = PreferenceUtils.getContentUuid(context); + if (TextUtils.isEmpty(contentUuid)) { + contentUuid = UUID.randomUUID().toString(); + PreferenceUtils.setContentUuid(context, contentUuid); + } + Content content = new Content(); + content.setUuid(contentUuid); + content.setUserAgent(BUILD_HANDSHAKE_CONTENT_USER_AGENT); + content.setDisplayName(BUILD_HANDSHAKE_CONTENT_DISPLAY_NAME); + content.setScreens(new Screen[]{screen}); + + String instanceId = PreferenceUtils.getInstanceId(context); + if (TextUtils.isEmpty(instanceId)) { + instanceId = UUID.randomUUID().toString(); + PreferenceUtils.setInstanceId(context, instanceId); + } + Handshake handshake = new Handshake(); + handshake.setType(BUILD_HANDSHAKE_TYPE); + handshake.setInstanceId(instanceId); + handshake.setContent(content); + return handshake; + } + + public static String buildArtboardPath(Context context, Artboard artboard) { + return "/artboards/" + artboard.getId() + + "@" + Math.round(DisplayUtils.getScreenDensity(context)) + + "x.png"; + } + + public static String buildArtboardHttpUrl(Context context, Artboard artboard) { + return PreferenceUtils.getMirror(context) + artboard.getPath() + + "?token=" + PreferenceUtils.getToken(context) + + "&t=" + System.currentTimeMillis(); + } + + public static String getCurrentSketch(Context context, MirrorInfo mirrorInfo) { + return !TextUtils.isEmpty(mirrorInfo.getName()) ? mirrorInfo.getName() + : context.getString(R.string.connect_status_default_sketch); + } + + public static List getArtboardsFromContent(Context context, Content content) { + List list = new ArrayList<>(); + + for (Page page : content.getContents().getPages()) { + list.addAll(Arrays.asList(page.getArtboards())); + } + + for (Artboard artboard : list) { + artboard.setPath(MirrorUtils.buildArtboardPath(context, artboard)); + } + + return list; + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/util/NetworkUtils.java b/app/src/main/java/com/zhihu/android/app/mirror/util/NetworkUtils.java new file mode 100644 index 0000000..cc4a462 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/util/NetworkUtils.java @@ -0,0 +1,46 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.util; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.text.TextUtils; + +import com.zhihu.android.app.mirror.R; + +public class NetworkUtils { + public static boolean isWifiConnected(Context context) { + ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo info = manager.getActiveNetworkInfo(); + return info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_WIFI; + } + + public static void setWifiEnabled(Context context, boolean enabled) { + ((WifiManager) context.getSystemService(Context.WIFI_SERVICE)).setWifiEnabled(enabled); + } + + public static String getCurrentSSID(Context context) { + WifiManager manager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + WifiInfo info = manager.getConnectionInfo(); + return info != null && !TextUtils.isEmpty(info.getSSID()) ? info.getSSID() : context.getString(R.string.connect_status_default_ssid); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/util/PreferenceUtils.java b/app/src/main/java/com/zhihu/android/app/mirror/util/PreferenceUtils.java new file mode 100644 index 0000000..a6c967c --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/util/PreferenceUtils.java @@ -0,0 +1,75 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.util; + +import android.content.Context; +import android.preference.PreferenceManager; + +public class PreferenceUtils { + private static final String MIRROR = "MIRROR"; + private static final String CONTENT_UUID = "CONTENT_UUID"; + private static final String INSTANCE_ID = "INSTANCE_ID"; + private static final String TOKEN = "TOKEN"; + private static final String DEVICE = "DEVICE"; + + public static String getMirror(Context context) { + return PreferenceManager.getDefaultSharedPreferences(context).getString(MIRROR, null); + } + + public static void setMirror(Context context, String mirror) { + PreferenceManager.getDefaultSharedPreferences(context) + .edit().putString(MIRROR, mirror).apply(); + } + + public static String getContentUuid(Context context) { + return PreferenceManager.getDefaultSharedPreferences(context).getString(CONTENT_UUID, null); + } + + public static void setContentUuid(Context context, String contentUuid) { + PreferenceManager.getDefaultSharedPreferences(context) + .edit().putString(CONTENT_UUID, contentUuid).apply(); + } + + public static String getInstanceId(Context context) { + return PreferenceManager.getDefaultSharedPreferences(context).getString(INSTANCE_ID, null); + } + + public static void setInstanceId(Context context, String instanceId) { + PreferenceManager.getDefaultSharedPreferences(context) + .edit().putString(INSTANCE_ID, instanceId).apply(); + } + + public static String getToken(Context context) { + return PreferenceManager.getDefaultSharedPreferences(context).getString(TOKEN, null); + } + + public static void setToken(Context context, String token) { + PreferenceManager.getDefaultSharedPreferences(context) + .edit().putString(TOKEN, token).apply(); + } + + public static String getDevice(Context context) { + return PreferenceManager.getDefaultSharedPreferences(context).getString(DEVICE, null); + } + + public static void setDevice(Context context, String device) { + PreferenceManager.getDefaultSharedPreferences(context) + .edit().putString(DEVICE, device).apply(); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/util/RxBus.java b/app/src/main/java/com/zhihu/android/app/mirror/util/RxBus.java new file mode 100644 index 0000000..67b476b --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/util/RxBus.java @@ -0,0 +1,48 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.util; + +import rx.Observable; +import rx.subjects.PublishSubject; +import rx.subjects.SerializedSubject; +import rx.subjects.Subject; + +public class RxBus { + private static final RxBus sRxBus = new RxBus(); + + public static RxBus getInstance() { + return sRxBus; + } + + private Subject mSubject = new SerializedSubject<>(PublishSubject.create()); + + private RxBus() {} + + public void post(Object object) { + mSubject.onNext(object); + } + + public Observable toObservable(Class eventType) { + return mSubject.ofType(eventType); + } + + public boolean hasObservers() { + return mSubject.hasObservers(); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/ArtboardLayout.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/ArtboardLayout.java new file mode 100644 index 0000000..a4685f5 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/ArtboardLayout.java @@ -0,0 +1,177 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget; + +import android.content.Context; +import android.graphics.Bitmap; +import android.net.Uri; +import android.util.AttributeSet; +import android.view.animation.LinearInterpolator; +import android.widget.FrameLayout; +import android.widget.ProgressBar; + +import com.davemorrissey.labs.subscaleview.ImageSource; +import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; +import com.facebook.common.executors.UiThreadImmediateExecutorService; +import com.facebook.common.references.CloseableReference; +import com.facebook.datasource.DataSource; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; +import com.facebook.imagepipeline.image.CloseableImage; +import com.facebook.imagepipeline.request.ImageRequest; +import com.facebook.imagepipeline.request.ImageRequestBuilder; + +import com.zhihu.android.app.mirror.R; +import com.zhihu.android.app.mirror.model.Artboard; +import com.zhihu.android.app.mirror.util.AnimUtils; +import com.zhihu.android.app.mirror.util.MirrorUtils; +import rx.Observable; +import rx.Subscriber; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; + +public class ArtboardLayout extends FrameLayout implements SubsamplingScaleImageView.OnImageEventListener { + private ArtboardView mArtboardView; + private ProgressBar mProgressBar; + + private Subscription mSubscription; + private LinearInterpolator mInterpolator; + + public ArtboardLayout(Context context) { + super(context); + } + + public ArtboardLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ArtboardLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void setArtboardViewCallback(ArtboardView.ArtboardViewCallback callback) { + mArtboardView.setArtboardViewCallback(callback); + } + + public void setArtboard(Artboard artboard) { + AnimUtils.showViewAlphaAnim(mProgressBar, mInterpolator, true); + + String url = MirrorUtils.buildArtboardHttpUrl(getContext(), artboard); + ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)).build(); + DataSource> dataSource = Fresco.getImagePipeline() + .fetchDecodedImage(request, getContext(), ImageRequest.RequestLevel.FULL_FETCH); + dataSource.subscribe(new BaseBitmapDataSubscriber() { + @Override + protected void onNewResultImpl(final Bitmap bitmap) { + if (bitmap == null || bitmap.isRecycled()) { + return; + } + + if (mSubscription != null) { + mSubscription.unsubscribe(); + } + + mSubscription = Observable.create( + new Observable.OnSubscribe() { + @Override + public void call(Subscriber subscriber) { + Bitmap copy = bitmap.copy(bitmap.getConfig(), true); + subscriber.onNext(copy); + subscriber.onCompleted(); + } + }) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + // DO NOTHING + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onNext(Bitmap bitmap) { + mArtboardView.setImage(ImageSource.bitmap(bitmap)); + } + }); + } + + @Override + protected void onFailureImpl(DataSource> dataSource) { + AnimUtils.showViewAlphaAnim(mProgressBar, mInterpolator, true); + } + }, UiThreadImmediateExecutorService.getInstance()); + } + + public boolean isScaling() { + return mArtboardView.isScaling(); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + mArtboardView = (ArtboardView) findViewById(R.id.artboard); + mProgressBar = (ProgressBar) findViewById(R.id.progress); + + mArtboardView.setOnImageEventListener(this); + mInterpolator = new LinearInterpolator(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + mArtboardView.setArtboardViewCallback(null); + mArtboardView.setOnImageEventListener(null); + if (mSubscription != null) { + mSubscription.unsubscribe(); + } + } + + @Override + public void onReady() { + // DO NOTHING + } + + @Override + public void onPreviewLoadError(Exception e) { + // DO NOTHING + } + + @Override + public void onTileLoadError(Exception e) { + // DO NOTHING + } + + @Override + public void onImageLoadError(Exception e) { + AnimUtils.showViewAlphaAnim(mProgressBar, mInterpolator, true); + } + + @Override + public void onImageLoaded() { + AnimUtils.showViewAlphaAnim(mProgressBar, mInterpolator, false); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/ArtboardPagerView.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/ArtboardPagerView.java new file mode 100644 index 0000000..e34202f --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/ArtboardPagerView.java @@ -0,0 +1,64 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget; + +import android.content.Context; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.MotionEvent; + +public class ArtboardPagerView extends ViewPager { + public interface ArtboardPagerViewDelegate { + boolean isArtboardPagerViewSwipeEnable(); + } + + private ArtboardPagerViewDelegate mDelegate; + + public ArtboardPagerView(Context context) { + super(context); + } + + public ArtboardPagerView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setArtboardPagerViewDelegate(ArtboardPagerViewDelegate delegate) { + mDelegate = delegate; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + if (mDelegate != null) { + return mDelegate.isArtboardPagerViewSwipeEnable() + && super.onInterceptTouchEvent(event); + } + + return super.onInterceptTouchEvent(event); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (mDelegate != null) { + return !mDelegate.isArtboardPagerViewSwipeEnable() + || super.onTouchEvent(event); + } + + return super.onTouchEvent(event); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/ArtboardView.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/ArtboardView.java new file mode 100644 index 0000000..4dabc79 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/ArtboardView.java @@ -0,0 +1,210 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.PointF; +import android.support.annotation.NonNull; +import android.support.v4.view.animation.PathInterpolatorCompat; +import android.util.AttributeSet; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; + +import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; + +import java.math.BigDecimal; + +import com.zhihu.android.app.mirror.R; +import com.zhihu.android.app.mirror.util.AnimUtils; +import com.zhihu.android.app.mirror.util.DisplayUtils; + +// https://github.com/nickbutcher/plaid/blob/master/app/src/main/java/io/plaidapp/ui/widget/ElasticDragDismissFrameLayout.java +public class ArtboardView extends SubsamplingScaleImageView implements View.OnTouchListener { + public interface ArtboardViewCallback { + void onDrag(ArtboardView view, float dragDismissDistance, float dragTo); + void onDragDismiss(ArtboardView view, boolean isDragDown); + } + + private float mDragDismissDistance; + private float mDragElacticity; + + private ObjectAnimator mTransYAnim; + private float mActionDownY; + private float mDragDistance; + private boolean mIsDragDown; // drag direction to screen bottom + + private ArtboardViewCallback mCallback; + private GestureDetector mGestureDetector; + + public ArtboardView(Context context) { + super(context); + init(null); + } + + public ArtboardView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + private void init(AttributeSet attrs) { + mDragDismissDistance = DisplayUtils.dp2px(getContext(), 112.0F); + mDragElacticity = 0.8F; + if (attrs != null) { + TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.ArtboardView); + mDragDismissDistance = array.getDimensionPixelOffset(R.styleable.ArtboardView_avDragDismissDistance, + (int) mDragDismissDistance); + mDragElacticity = array.getFloat(R.styleable.ArtboardView_avDragElasticity, mDragElacticity); + array.recycle(); + } + + mGestureDetector = new GestureDetector(getContext(), buildSimpleOnGestureListener()); + setOnTouchListener(this); + } + + // always scale to the screen width side + private GestureDetector.SimpleOnGestureListener buildSimpleOnGestureListener() { + return new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onDoubleTap(MotionEvent event) { + if (!isReady()) { + return true; + } + + float scale; + float screenWidth = DisplayUtils.getScreenWidth(getContext()); + if (getSWidth() >= getSHeight()) { + if (getSWidth() >= screenWidth) { + scale = getMinScale(); + } else { + scale = screenWidth / (float) getSWidth(); + } + } else { + scale = screenWidth / (float) getSWidth(); + } + + animateScaleAndCenter(!isScaling() ? scale : getMinScale(), new PointF(event.getX(), event.getY())) + .withDuration(AnimUtils.DURATION) + .withEasing(SubsamplingScaleImageView.EASE_OUT_QUAD) + .withInterruptible(false) + .start(); + return true; + } + }; + } + + public void setArtboardViewCallback(ArtboardViewCallback callback) { + mCallback = callback; + } + + // ensure getScale() == getMinScale() + public boolean isScaling() { + float scale; + float minScale; + try { + BigDecimal decimal = new BigDecimal(getScale()); + scale = decimal.setScale(2, BigDecimal.ROUND_FLOOR).floatValue(); + decimal = new BigDecimal(getMinScale()); + minScale = decimal.setScale(2, BigDecimal.ROUND_FLOOR).floatValue(); + return scale > minScale; + } catch (NumberFormatException e) { + e.printStackTrace(); + return false; + } + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mCallback = null; + } + + @Override + public boolean onTouchEvent(@NonNull MotionEvent event) { + boolean result = super.onTouchEvent(event); + + if (isImageLoaded() && !isScaling()) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + if (mTransYAnim != null && mTransYAnim.isRunning()) { + mTransYAnim.cancel(); + } + mActionDownY = event.getY(); + mDragDistance = 0.0F; + mIsDragDown = false; + break; + case MotionEvent.ACTION_MOVE: + onActionMove(event); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + onActionRelease(); + break; + default: + break; + } + } + + return result; + } + + private void onActionMove(MotionEvent event) { + mDragDistance = event.getY() - mActionDownY; + mIsDragDown = mDragDistance > 0.0F; + + float dragFraction = (float) Math.log10(1.0F + (Math.abs(mDragDistance) / mDragDismissDistance)); + float dragTo = dragFraction * mDragDismissDistance * mDragElacticity; + setTranslationY(mIsDragDown ? dragTo : -dragTo); + if (mCallback != null) { + mCallback.onDrag(this, mDragDismissDistance, dragTo); + } + } + + private void onActionRelease() { + if (mCallback != null && Math.abs(mDragDistance) > mDragDismissDistance) { + mCallback.onDragDismiss(this, mIsDragDown); + } else { + if (mTransYAnim != null && mTransYAnim.isRunning()) { + mTransYAnim.cancel(); + } + + mTransYAnim = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, getTranslationY(), 0.0F); + mTransYAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float dragTo = (Float) animation.getAnimatedValue(); + if (mCallback != null) { + mCallback.onDrag(ArtboardView.this, mDragDismissDistance, dragTo); + } + } + }); + mTransYAnim.setDuration(getResources().getInteger(android.R.integer.config_shortAnimTime)); + mTransYAnim.setInterpolator(PathInterpolatorCompat.create(0.4F, 0.0F, 0.2F, 1.0F)); + mTransYAnim.start(); + } + } + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + return mGestureDetector.onTouchEvent(motionEvent); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/ConnectStatusLayout.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/ConnectStatusLayout.java new file mode 100644 index 0000000..8d44b1a --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/ConnectStatusLayout.java @@ -0,0 +1,88 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget; + +import android.content.Context; +import android.support.annotation.IntDef; +import android.support.v7.widget.AppCompatTextView; +import android.util.AttributeSet; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.zhihu.android.app.mirror.R; +import com.zhihu.android.app.mirror.util.DisplayUtils; + +public class ConnectStatusLayout extends LinearLayout { + public static final int CONNECTING = 0x00; + public static final int CONNECTED = 0x01; + public static final int DISCONNECTED = 0x02; + + @IntDef({CONNECTING, CONNECTED, DISCONNECTED}) + @Retention(RetentionPolicy.SOURCE) + @Target({ElementType.FIELD, ElementType.PARAMETER}) + public @interface ConnectStatus {} + + private PointView mPointView; + private AppCompatTextView mMessageView; + + public ConnectStatusLayout(Context context) { + super(context); + } + + public ConnectStatusLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ConnectStatusLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void setConnectStatus(@ConnectStatus int status, String tmpl) { + switch (status) { + case CONNECTED: + mPointView.stopBreath(); + mMessageView.setText(getResources().getString(R.string.connect_status_connected, tmpl)); + break; + case DISCONNECTED: + mPointView.startBreath(); + mMessageView.setText(getResources().getString(R.string.connect_status_disconnected, tmpl)); + break; + case CONNECTING: + default: + mPointView.startBreath(); + mMessageView.setText(getResources().getString(R.string.connect_status_connecting, tmpl)); + break; + } + } + + @Override + public void onFinishInflate() { + super.onFinishInflate(); + ((FrameLayout.LayoutParams) getLayoutParams()).topMargin = DisplayUtils.getStatusBarHeight(getContext()); + requestLayout(); + + mPointView = (PointView) findViewById(R.id.point); + mMessageView = (AppCompatTextView) findViewById(R.id.message); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/DownsamplingView.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/DownsamplingView.java new file mode 100644 index 0000000..fa5e889 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/DownsamplingView.java @@ -0,0 +1,123 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget; + +import android.content.Context; +import android.graphics.PointF; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.animation.DecelerateInterpolator; + +import com.facebook.drawee.drawable.ScalingUtils; +import com.facebook.drawee.view.SimpleDraweeView; + +import com.zhihu.android.app.mirror.R; +import com.zhihu.android.app.mirror.util.DisplayUtils; + +public class DownsamplingView extends SimpleDraweeView { + private static final float W_H_RATIO = 1.0F; + private static final int FADE_DURATION = 300; + + private static final float ALPHA_TO = 0.87F; + private static final float SCALE_TO = 0.99F; + private static final long DURATION = 100L; + + private int mDownsamplingViewWidth; + private int mDownsamplingViewHeight; + + public DownsamplingView(Context context) { + super(context); + } + + public DownsamplingView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DownsamplingView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public int getDownsamplingViewWidth() { + return mDownsamplingViewWidth; + } + + public int getDownsamplingViewHeight() { + return mDownsamplingViewHeight; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + int gridSpanCount = getResources().getInteger(R.integer.grid_span_count); + float width = DisplayUtils.getScreenWidth(getContext()) / gridSpanCount; + float height = width / W_H_RATIO; + mDownsamplingViewWidth = (int) width; + mDownsamplingViewHeight = (int) height; + + getHierarchy().setActualImageScaleType(ScalingUtils.ScaleType.FOCUS_CROP); + getHierarchy().setActualImageFocusPoint(new PointF(0.5F, 0.0F)); // top center + getHierarchy().setFadeDuration(FADE_DURATION); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + widthMeasureSpec = MeasureSpec.makeMeasureSpec(mDownsamplingViewWidth, MeasureSpec.EXACTLY); + heightMeasureSpec = MeasureSpec.makeMeasureSpec(mDownsamplingViewHeight, MeasureSpec.EXACTLY); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + onActionDown(event); + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + onActionRelease(event); + break; + default: + break; + } + + return true; + } + + private void onActionDown(MotionEvent event) { + setPivotX(event.getX()); + setPivotY(event.getY()); + animate().alpha(ALPHA_TO) + .scaleX(SCALE_TO).scaleY(SCALE_TO) + .setDuration(DURATION) + .setInterpolator(new DecelerateInterpolator()) + .start(); + } + + @SuppressWarnings("unused") + private void onActionRelease(MotionEvent event) { + animate().alpha(1.0F) + .scaleX(1.0F).scaleY(1.0F) + .setDuration(DURATION) + .setInterpolator(new DecelerateInterpolator()) + .start(); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/MirrorNameView.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/MirrorNameView.java new file mode 100644 index 0000000..e102c9d --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/MirrorNameView.java @@ -0,0 +1,89 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget; + +import android.content.Context; +import android.support.v7.widget.AppCompatTextView; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.animation.DecelerateInterpolator; + +import com.zhihu.android.app.mirror.util.DisplayUtils; + +public class MirrorNameView extends AppCompatTextView { + private static final float ALPHA_TO = 0.87F; + private static final float SCALE_TO = 0.99F; + private static final long DURATION = 100L; + + public MirrorNameView(Context context) { + super(context); + } + + public MirrorNameView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public MirrorNameView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + setMinWidth(DisplayUtils.getScreenWidth(getContext())); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + onActionDown(event); + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + onActionRelease(event); + break; + default: + break; + } + + return true; + } + + private void onActionDown(MotionEvent event) { + setPivotX(event.getX()); + setPivotY(event.getY()); + animate().alpha(ALPHA_TO) + .scaleX(SCALE_TO).scaleY(SCALE_TO) + .setDuration(DURATION) + .setInterpolator(new DecelerateInterpolator()) + .start(); + } + + @SuppressWarnings("unused") + private void onActionRelease(MotionEvent event) { + animate().alpha(1.0F) + .scaleX(1.0F).scaleY(1.0F) + .setDuration(DURATION) + .setInterpolator(new DecelerateInterpolator()) + .start(); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/PointView.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/PointView.java new file mode 100644 index 0000000..8474219 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/PointView.java @@ -0,0 +1,119 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.view.View; + +import com.zhihu.android.app.mirror.R; +import com.zhihu.android.app.mirror.util.DisplayUtils; + +public class PointView extends View { + private Paint mPaint; + private int mAlpha; + private int mRadius; + + private float mCurrentAlpha; + private float mDeltaFraction; + private long mPreDrawTime; + private boolean mIsRunning; + private boolean mIsReverse; + + public PointView(Context context) { + super(context); + init(null); + } + + public PointView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public PointView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + private void init(AttributeSet attrs) { + mAlpha = 255; + mRadius = DisplayUtils.dp2px(getContext(), 8.0F); + int color = Color.WHITE; + int duration = 255; + if (attrs != null) { + TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.PointView); + mAlpha = array.getInt(R.styleable.PointView_pvAlpha, mAlpha); + mRadius = array.getDimensionPixelSize(R.styleable.PointView_pvRadius, mRadius); + color = array.getColor(R.styleable.PointView_pvColor, color); + duration = array.getInt(R.styleable.PointView_pvDuration, duration); + array.recycle(); + } + + mCurrentAlpha = 255.0F; + mDeltaFraction = (mCurrentAlpha - mAlpha) / duration; + mIsRunning = false; + mIsReverse = false; + + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.FILL); + mPaint.setAlpha((int) mCurrentAlpha); + mPaint.setColor(color); + } + + public void startBreath() { + mIsRunning = true; + mPreDrawTime = System.currentTimeMillis(); + invalidate(); + } + + public void stopBreath() { + mIsRunning = false; + } + + @Override + protected void onDraw(Canvas canvas) { + if (!mIsRunning) { + mCurrentAlpha = 255.0F; + mPaint.setAlpha((int) mCurrentAlpha); + canvas.drawCircle(getPivotX(), getPivotY(), mRadius, mPaint); + return; + } + + float delta = (System.currentTimeMillis() - mPreDrawTime) * mDeltaFraction; + if (!mIsReverse && mCurrentAlpha - delta < mAlpha) { + mCurrentAlpha = mAlpha; + mIsReverse = true; + } else if (mIsReverse && mCurrentAlpha + delta > 255.0F) { + mCurrentAlpha = 255.0F; + mIsReverse = false; + } else { + mCurrentAlpha = !mIsReverse ? mCurrentAlpha - delta : mCurrentAlpha + delta; + } + + mPaint.setAlpha((int) mCurrentAlpha); + canvas.drawCircle(getPivotX(), getPivotY(), mRadius, mPaint); + mPreDrawTime = System.currentTimeMillis(); + invalidate(); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/adapter/ArtboardListAdapter.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/adapter/ArtboardListAdapter.java new file mode 100644 index 0000000..df46092 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/adapter/ArtboardListAdapter.java @@ -0,0 +1,83 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget.adapter; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.util.List; + +import com.zhihu.android.app.mirror.R; +import com.zhihu.android.app.mirror.model.Artboard; +import com.zhihu.android.app.mirror.util.DisplayUtils; +import com.zhihu.android.app.mirror.weiget.holder.DownsamplingHolder; +import com.zhihu.android.app.mirror.weiget.holder.PlaceHolder; + +public class ArtboardListAdapter extends RecyclerView.Adapter { + private static final int VIEW_TYPE_PLACE_HOLDER = 0x00; + private static final int VIEW_TYPE_DOWNSAMPLING_HOLDER = 0x01; + + private Context mContext; + private List mList; + + public ArtboardListAdapter(Context context, List list) { + mContext = context; + mList = list; + } + + @Override + public int getItemCount() { + return mList.size() + 2; + } + + @Override + public int getItemViewType(int position) { + if (0 < position && position <= mList.size()) { + return VIEW_TYPE_DOWNSAMPLING_HOLDER; + } else { + return VIEW_TYPE_PLACE_HOLDER; + } + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == VIEW_TYPE_DOWNSAMPLING_HOLDER) { + View view = LayoutInflater.from(mContext).inflate(R.layout.recycler_item_downsampling, parent, false); + return new DownsamplingHolder(view); + } else { + View view = LayoutInflater.from(mContext).inflate(R.layout.recycler_item_place, parent, false); + return new PlaceHolder(view); + } + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + if (position <= 0) { + ((PlaceHolder) holder).bind(DisplayUtils.getStatusBarHeight(mContext) + + mContext.getResources().getDimensionPixelSize(R.dimen.connect_status_height)); + } else if (0 < position && position <= mList.size()) { + ((DownsamplingHolder) holder).bind(mList.get(position - 1)); + } else if (mList.size() < position) { + ((PlaceHolder) holder).bind(DisplayUtils.getNavigationBarHeight(mContext)); + } + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/adapter/ArtboardPagerAdapter.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/adapter/ArtboardPagerAdapter.java new file mode 100644 index 0000000..20fc0cc --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/adapter/ArtboardPagerAdapter.java @@ -0,0 +1,127 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget.adapter; + +import android.support.annotation.Nullable; +import android.support.v4.view.PagerAdapter; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.util.List; + +import com.zhihu.android.app.mirror.R; +import com.zhihu.android.app.mirror.model.Artboard; +import com.zhihu.android.app.mirror.weiget.ArtboardLayout; +import com.zhihu.android.app.mirror.weiget.ArtboardView; + +public class ArtboardPagerAdapter extends PagerAdapter { + private List mList; + + private ArtboardLayout mCurrentLayout; + private ArtboardView.ArtboardViewCallback mCallback; + + public ArtboardPagerAdapter(List list) { + mList = list; + } + + public void setArtboardViewCallback(ArtboardView.ArtboardViewCallback callback) { + mCallback = callback; + } + + @Nullable + public ArtboardLayout getCurrentLayout() { + return mCurrentLayout; + } + + @Override + public void setPrimaryItem(ViewGroup container, int position, Object object) { + super.setPrimaryItem(container, position, object); + if (!(object instanceof ArtboardLayout)) { + return; + } + + ArtboardLayout layout = (ArtboardLayout) object; + if (layout != mCurrentLayout && mCurrentLayout != null) { + mCurrentLayout.setArtboardViewCallback(null); + } + + mCurrentLayout = layout; + mCurrentLayout.setArtboardViewCallback(mCallback); + } + + @Override + public int getItemPosition(Object object) { + if (mList.size() <= 0) { + return POSITION_NONE; + } + + View view = (View) object; + int position = (int) view.getTag(R.id.artboard_position); + String id = (String) view.getTag(R.id.artboard_id); + + int index = -1; + for (int i = 0; i < mList.size(); i++) { + Artboard artboard = mList.get(i); + if (TextUtils.equals(artboard.getId(), id)) { + index = i; + break; + } + } + + if (index != position) { + return POSITION_NONE; + } else { + Artboard artboard = mList.get(position); + boolean needUpdate = artboard.isNeedUpdateInPager(); + artboard.setNeedUpdateInPager(false); + return needUpdate ? POSITION_NONE : POSITION_UNCHANGED; + } + } + + @Override + public int getCount() { + return mList.size(); + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return view == object; + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + ArtboardLayout layout = (ArtboardLayout) LayoutInflater.from(container.getContext()) + .inflate(R.layout.pager_item_artboard, container, false); + container.addView(layout); + + Artboard artboard = mList.get(position); + layout.setTag(R.id.artboard_position, position); + layout.setTag(R.id.artboard_id, artboard.getId()); + layout.setArtboard(artboard); + + return layout; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + container.removeView((View) object); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/adapter/MirrorListAdapter.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/adapter/MirrorListAdapter.java new file mode 100644 index 0000000..90ff6d5 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/adapter/MirrorListAdapter.java @@ -0,0 +1,83 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget.adapter; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.util.List; + +import com.zhihu.android.app.mirror.R; +import com.zhihu.android.app.mirror.model.MirrorInfo; +import com.zhihu.android.app.mirror.util.DisplayUtils; +import com.zhihu.android.app.mirror.weiget.holder.MirrorInfoHolder; +import com.zhihu.android.app.mirror.weiget.holder.PlaceHolder; + +public class MirrorListAdapter extends RecyclerView.Adapter { + private static final int VIEW_TYPE_PLACE_HOLDER = 0x00; + private static final int VIEW_TYPE_MIRROR_INFO_HOLDER = 0x01; + + private Context mContext; + private List mList; + + public MirrorListAdapter(Context context, List list) { + mContext = context; + mList = list; + } + + @Override + public int getItemCount() { + return mList.size() + 2; + } + + @Override + public int getItemViewType(int position) { + if (0 < position && position <= mList.size()) { + return VIEW_TYPE_MIRROR_INFO_HOLDER; + } else { + return VIEW_TYPE_PLACE_HOLDER; + } + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == VIEW_TYPE_MIRROR_INFO_HOLDER) { + View view = LayoutInflater.from(mContext).inflate(R.layout.recycler_item_mirror, parent, false); + return new MirrorInfoHolder(view); + } else { + View view = LayoutInflater.from(mContext).inflate(R.layout.recycler_item_place, parent, false); + return new PlaceHolder(view); + } + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + if (position <= 0) { + ((PlaceHolder) holder).bind(DisplayUtils.getStatusBarHeight(mContext) + + mContext.getResources().getDimensionPixelSize(R.dimen.connect_status_height)); + } else if (0 < position && position <= mList.size()) { + ((MirrorInfoHolder) holder).bind(mList.get(position - 1)); + } else if (mList.size() < position) { + ((PlaceHolder) holder).bind(DisplayUtils.getNavigationBarHeight(mContext)); + } + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/ArtboardHolder.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/ArtboardHolder.java new file mode 100644 index 0000000..7617ff1 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/ArtboardHolder.java @@ -0,0 +1,38 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget.holder; + +import android.support.v7.widget.RecyclerView; +import android.view.View; + +import com.zhihu.android.app.mirror.model.Artboard; +import com.zhihu.android.app.mirror.weiget.ArtboardLayout; + +public class ArtboardHolder extends RecyclerView.ViewHolder { + private ArtboardLayout mArtboardLayout; + + public ArtboardHolder(View view) { + super(view); + mArtboardLayout = (ArtboardLayout) view; + } + + public void bind(Artboard artboard) { + mArtboardLayout.setArtboard(artboard); + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/DownsamplingHolder.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/DownsamplingHolder.java new file mode 100644 index 0000000..9c1658e --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/DownsamplingHolder.java @@ -0,0 +1,71 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget.holder; + +import android.net.Uri; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.backends.pipeline.PipelineDraweeController; +import com.facebook.imagepipeline.common.ResizeOptions; +import com.facebook.imagepipeline.request.ImageRequest; +import com.facebook.imagepipeline.request.ImageRequestBuilder; + +import com.zhihu.android.app.mirror.R; +import com.zhihu.android.app.mirror.event.ArtboardSelectedEvent; +import com.zhihu.android.app.mirror.model.Artboard; +import com.zhihu.android.app.mirror.util.MirrorUtils; +import com.zhihu.android.app.mirror.util.RxBus; +import com.zhihu.android.app.mirror.weiget.DownsamplingView; + +public class DownsamplingHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + private DownsamplingView mDownsamplingView; + private Artboard mArtboard; + + public DownsamplingHolder(View view) { + super(view); + + mDownsamplingView = (DownsamplingView) view.findViewById(R.id.downsampling); + mDownsamplingView.setOnClickListener(this); + } + + public void bind(Artboard artboard) { + mArtboard = artboard; + + String url = MirrorUtils.buildArtboardHttpUrl(mDownsamplingView.getContext(), mArtboard); + int mResizeWidth = mDownsamplingView.getDownsamplingViewWidth(); + int mResizeHeight = mDownsamplingView.getDownsamplingViewHeight(); + ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)) + .setResizeOptions(new ResizeOptions(mResizeWidth, mResizeHeight)) + .build(); + PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder() + .setOldController(mDownsamplingView.getController()) + .setImageRequest(request) + .build(); + mDownsamplingView.setController(controller); + } + + @Override + public void onClick(View view) { + if (view == mDownsamplingView) { + RxBus.getInstance().post(new ArtboardSelectedEvent(mArtboard)); + } + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/MirrorInfoHolder.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/MirrorInfoHolder.java new file mode 100644 index 0000000..04b88e1 --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/MirrorInfoHolder.java @@ -0,0 +1,52 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget.holder; + +import android.support.v7.widget.RecyclerView; +import android.view.View; + +import com.zhihu.android.app.mirror.R; +import com.zhihu.android.app.mirror.event.MirrorSelectedEvent; +import com.zhihu.android.app.mirror.model.MirrorInfo; +import com.zhihu.android.app.mirror.util.RxBus; +import com.zhihu.android.app.mirror.weiget.MirrorNameView; + +public class MirrorInfoHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + private MirrorNameView mNameView; + private MirrorInfo mMirrorInfo; + + public MirrorInfoHolder(View view) { + super(view); + + mNameView = (MirrorNameView) view.findViewById(R.id.name); + mNameView.setOnClickListener(this); + } + + public void bind(MirrorInfo mirrorInfo) { + mMirrorInfo = mirrorInfo; + mNameView.setText(mMirrorInfo.getName()); + } + + @Override + public void onClick(View view) { + if (view == mNameView) { + RxBus.getInstance().post(new MirrorSelectedEvent(mMirrorInfo)); + } + } +} diff --git a/app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/PlaceHolder.java b/app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/PlaceHolder.java new file mode 100644 index 0000000..67623dd --- /dev/null +++ b/app/src/main/java/com/zhihu/android/app/mirror/weiget/holder/PlaceHolder.java @@ -0,0 +1,36 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +package com.zhihu.android.app.mirror.weiget.holder; + +import android.support.v7.widget.RecyclerView; +import android.view.View; + +public class PlaceHolder extends RecyclerView.ViewHolder { + private View mView; + + public PlaceHolder(View view) { + super(view); + mView = view; + } + + public void bind(int height) { + mView.getLayoutParams().height = height; + mView.requestLayout(); + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..45c0870 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/layout_artboard_list.xml b/app/src/main/res/layout/layout_artboard_list.xml new file mode 100644 index 0000000..f35e817 --- /dev/null +++ b/app/src/main/res/layout/layout_artboard_list.xml @@ -0,0 +1,27 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_artboard_pager.xml b/app/src/main/res/layout/layout_artboard_pager.xml new file mode 100644 index 0000000..d90bff0 --- /dev/null +++ b/app/src/main/res/layout/layout_artboard_pager.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_connect_status.xml b/app/src/main/res/layout/layout_connect_status.xml new file mode 100644 index 0000000..aaf6877 --- /dev/null +++ b/app/src/main/res/layout/layout_connect_status.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/pager_item_artboard.xml b/app/src/main/res/layout/pager_item_artboard.xml new file mode 100644 index 0000000..0882fd9 --- /dev/null +++ b/app/src/main/res/layout/pager_item_artboard.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_item_downsampling.xml b/app/src/main/res/layout/recycler_item_downsampling.xml new file mode 100644 index 0000000..c0b6b6c --- /dev/null +++ b/app/src/main/res/layout/recycler_item_downsampling.xml @@ -0,0 +1,31 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_item_mirror.xml b/app/src/main/res/layout/recycler_item_mirror.xml new file mode 100644 index 0000000..ef04d9b --- /dev/null +++ b/app/src/main/res/layout/recycler_item_mirror.xml @@ -0,0 +1,40 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_item_place.xml b/app/src/main/res/layout/recycler_item_place.xml new file mode 100644 index 0000000..f11f11d --- /dev/null +++ b/app/src/main/res/layout/recycler_item_place.xml @@ -0,0 +1,27 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4a57de5e95ccc846daf4a4050d0128f265ed7a32 GIT binary patch literal 2270 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..126608bdd81e959f449b08c9ae49f2dad1c62327 GIT binary patch literal 9304 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 GBmNH?r7?N{ literal 0 HcmV?d00001 diff --git a/app/src/main/res/values-v23/dimens.xml b/app/src/main/res/values-v23/dimens.xml new file mode 100644 index 0000000..a35837d --- /dev/null +++ b/app/src/main/res/values-v23/dimens.xml @@ -0,0 +1,25 @@ + + + + + + + 24dp + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000..b0ffb2a --- /dev/null +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,31 @@ + + + + + + + Mirror + + 正在连接 %1$s… + 已连接至 %1$s + %1$s 已断开连接 + WIFI + Sketch + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml new file mode 100644 index 0000000..8cfc17e --- /dev/null +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -0,0 +1,31 @@ + + + + + + + Mirror + + 正在連接 %1$s… + 已連接至 %1$s + %1$s 已斷開連接 + WIFI + Sketch + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rMO/strings.xml b/app/src/main/res/values-zh-rMO/strings.xml new file mode 100644 index 0000000..8cfc17e --- /dev/null +++ b/app/src/main/res/values-zh-rMO/strings.xml @@ -0,0 +1,31 @@ + + + + + + + Mirror + + 正在連接 %1$s… + 已連接至 %1$s + %1$s 已斷開連接 + WIFI + Sketch + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000..8cfc17e --- /dev/null +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,31 @@ + + + + + + + Mirror + + 正在連接 %1$s… + 已連接至 %1$s + %1$s 已斷開連接 + WIFI + Sketch + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..893c589 --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..4be231e --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,27 @@ + + + + + + + @android:color/black + #66000000 + @android:color/white + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..a1c44a1 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,25 @@ + + + + + + 25dp + 8dp + + \ No newline at end of file diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml new file mode 100644 index 0000000..6416d58 --- /dev/null +++ b/app/src/main/res/values/ids.xml @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml new file mode 100644 index 0000000..f398815 --- /dev/null +++ b/app/src/main/res/values/integers.xml @@ -0,0 +1,26 @@ + + + + + + + 3 + 1 + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..c49247f --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,31 @@ + + + + + + + Mirror + + Connecting %1$s… + %1$s connected + %1$s disconnected + WIFI + Sketch + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..38271f6 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,29 @@ + + + + + + + + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..764c3a3 --- /dev/null +++ b/build.gradle @@ -0,0 +1,37 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:2.2.2' + } +} + +allprojects { + repositories { + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/example.png b/example.png new file mode 100644 index 0000000000000000000000000000000000000000..306aa58f88c5563eaf2a33e80dcfc3da818e61de GIT binary patch literal 78296 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
TkKm|M%PtVaSBO&0j{1-b(|S`PHq4@;I@| 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 JP%dW~`aj7Dl+OSF literal 0 HcmV?d00001 diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..18fbf9d --- /dev/null +++ b/gradle.properties @@ -0,0 +1,35 @@ +# +# Mirror - Yet another Sketch Mirror App for Android. +# Copyright (C) 2016 Zhihu Inc. +# +# 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 3 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, see . +# + +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..13372aef5e24af05341d49695ee84e5f9b594659 GIT binary patch literal 53636 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ⅅ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 H-+%o-(+5aQ literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..05bd07e --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,24 @@ +# +# Mirror - Yet another Sketch Mirror App for Android. +# Copyright (C) 2016 Zhihu Inc. +# +# 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 3 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, see . +# + +#Mon Dec 28 10:00:20 PST 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..53e81ba --- /dev/null +++ b/settings.gradle @@ -0,0 +1,19 @@ +/* + * Mirror - Yet another Sketch Mirror App for Android. + * Copyright (C) 2016 Zhihu Inc. + * + * 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 3 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, see . + */ + +include ':app'