[
  {
    "path": ".gitignore",
    "content": "logs\ntarget\ntmp\n.history\ndist\n/out\n/RUNNING_PID\n/.ivy*\n\n# Keys\n/debug.properties\n/ninecards.properties\n/debug.keystore\n/local.properties\n/modules/app/local.properties\n\n# Crashlytics\n/modules/app/crashlytics/fabric.properties\n/modules/app/crashlytics/CrashlyticsManifest.xml\n/modules/app/src/main/assets/crashlytics-build.properties\n/modules/app/src/main/res/values/com_crashlytics_export_strings.xml\n\n# sbt specific\n/.sbt\n.cache/\n.history/\n.lib/\ndist/*\ntarget/\nlib_managed/\nsrc_managed/\nproject/boot/\nproject/plugins/project/\nproject/project\nproject/target\n/.activator\nfabric.properties\n\n# Scala-IDE specific\n.scala_dependencies\n.worksheet\n\n#Eclipse specific\n.classpath\n.project\n.cache\n.settings/\n\n#IntelliJ IDEA specific\n.idea/\n/.idea_modules\n/.idea\n/*.iml\n\n#Proguard\nproguard-sbt.txt\n\n#Gen\nmodules/api/gen/\nmodules/repository/gen/\n\n#Keys\n\ntravis-deploy-key\ntravis-deploy-key.pub"
  },
  {
    "path": ".scalafmt.conf",
    "content": "style = defaultWithAlign\nmaxColumn = 100\n\ncontinuationIndent.callSite = 2\n\nnewlines {\n  sometimesBeforeColonInMethodReturnType = false\n}\n\nalign {\n  arrowEnumeratorGenerator = false\n  ifWhileOpenParen = false\n  openParenCallSite = false\n  openParenDefnSite = false\n}\n\ndocstrings = JavaDoc\n\nrewrite {\n  rules = [SortImports, RedundantBraces]\n  redundantBraces.maxLines = 1\n}"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: false\nlanguage: scala\njdk:\n  - oraclejdk8\nscala:\n  - 2.11.7\naddons:\n  apt:\n    packages:\n      - libc6-i386\n      - lib32z1\n      - lib32stdc++6\nbefore_install:\n  - if [ \"$TRAVIS_BRANCH\" = \"master\" -a \"$TRAVIS_PULL_REQUEST\" = \"false\" ]; then bash\n    scripts/decrypt-keys.sh; fi\n  - export PATH=${PATH}:./vendor/bundle\n  - wget http://dl.google.com/android/android-sdk_r24-linux.tgz\n  - tar xf android-sdk_r24-linux.tgz\n  - export ANDROID_HOME=$PWD/android-sdk-linux\n  - export ANDROID_SDK_HOME=$PWD/android-sdk-linux\n  - export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools\n  - echo yes | android update sdk --all --filter platform-tools --no-ui\n  - echo yes | android update sdk --all --filter build-tools-25.0.0 --no-ui\n  - echo yes | android update sdk --all --filter android-24 --no-ui\n  - echo yes | android update sdk --all --filter extra-android-support --no-ui\n  - echo yes | android update sdk --all --filter extra-android-m2repository --no-ui\n  - echo yes | android update sdk --all --filter extra-google-m2repository --no-ui\ninstall:\n  - rvm use 2.2.3 --install --fuzzy\n  - gem update --system\n  - gem install sass\n  - gem install jekyll -v 3.2.1\nscript:\n  - sbt ++$TRAVIS_SCALA_VERSION \"project commons\" coverage test\n  - sbt ++$TRAVIS_SCALA_VERSION \"project api\" coverage test\n  - sbt ++$TRAVIS_SCALA_VERSION \"project repository\" coverage test\n  - sbt ++$TRAVIS_SCALA_VERSION \"project services\" coverage test\n  - sbt ++$TRAVIS_SCALA_VERSION \"project process\" coverage test\n  - sbt ++$TRAVIS_SCALA_VERSION \"project app\" coverage test\nafter_success:\n  - sbt ++$TRAVIS_SCALA_VERSION \"project tests\" test:coverageAggregate\n  - bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN}\n  - if [ \"$TRAVIS_BRANCH\" = \"master\" -a \"$TRAVIS_PULL_REQUEST\" = \"false\" ]; then bash\n    scripts/publishMicrosite.sh; fi\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## 2017/02/06 - Version Name: 2.0.11-rc2 - Version Code: 68\n\n* FlowUp have to be activated from Developer Options\n\n## 2017/01/30 - Version Name: 2.0.10-rc1 - Version Code: 67\n\n* Improved the experience when the user back to the launcher from App Drawer\n* New technical documentation\n* Changed URL for sharing collections\n* Some bugs fixed in UI\n\n## 2016/12/23 - Version Name: 2.0.9-beta - Version Code: 66\n\n* The user can add Bluetooth devices to Moment\n* Improved Widgets screen\n* Added Apptentive\n* The user can disable Google Analytics\n* Added tests for jobs \n* Some bugs fixed in UI\n\n## 2016/12/16 - Version Name: 2.0.8-beta - Version Code: 65\n\n* Drag&Drop for managing widgets\n* Share from other apps have been improved\n* Bugs fixed in shortcuts\n* Bugs fixed in subscriptions\n* Added tests for jobs \n* Some bugs fixed in UI\n\n## 2016/12/02 - Version Name: 2.0.7-beta - Version Code: 64\n\n* New About screen with Scala libraries and Team\n* Number of views in Public Collections is updated when the user adds it to his collections\n* New actions to access to Google Play and Phone in AppDrawer\n* Fixed some problems with AppDrawer in empty lists\n* Fixed problems in Wizard when some steps launch an exception\n* Added tests for jobs \n* Some bugs fixed in UI\n\n## 2016/11/28 - Version Name: 2.0.6-alpha - Version Code: 63\n\n* Removed clock in Moment bar\n* Added options to menu: wallpaper, settings and widgets\n* Upgrade libraries: SBT-Android plugin, Cats and Monix\n* Added tests for jobs \n* Some bugs fixed in UI\n\n## 2016/11/21 - Version Name: 2.0.5-alpha - Version Code: 62\n\n* We have removed the collapse toolbar in Collections Screen\n* Wallpaper is moved when the user swipe right/left in workspaces. You can be disabled this behavior in preferences\n* Bug fixed related to the app opens Wizard recursively\n* English texts reviewed\n* New message in Car, Music and Out & About Moment for explaining the special conditions of this moments\n* All events tracked in Google Analytics\n* Added tests for jobs \n\n## 2016/11/16 - Version Name: 2.0.4-alpha - Version Code: 61\n\n* New Wizards Inline. 9Cards Team shows you how you can use the launcher\n* The user can filter apps and contacts in the dialogs when he wants to add it to the collection\n* FlowUp integrated. Thanks to Pedro of Karumi\n* Now, the dialogs are using BottomSheetFragment in order to the app has the appearance like Nougat\n* New header in Moment Dialog\n* New events tracked in Google Analytics\n* Libraries upgraded: Android API to 24, Android Support. Google Services and Macroid 2.0\n* Added tests for jobs \n* Some bugs fixed in UI\n\n## 2016/11/09 - Version Name: 2.0.3-alpha - Version Code: 60\n\n* Changes in the Wizard: new design for loading previous configuration and some changes in new configuration\n* Now you can select several multiple apps in AppsDialog for adding to collection\n* Bugs fixed in Dark Theme\n* We have created the first version of microsite\n* First revision of all text in the app\n* New Developer Options (remember you have to do long-click in Settings button for activating Dev Ops)\n* New events tracked in Google Analytics\n* Added test for jobs in Launcher screen\n* Some bugs fixed in UI\n\n## 2016/10/31 - Version Name: 2.0.2-alpha - Version Code: 59\n\n* New design of App Drawer. Now the tabs are Applications and Contacts and you can filter from the new option menu\n* We have fixed the problems with the widgets. The widgets are updated when the user changes the size and loaded fine the first time\n* Bugs fixed on right drawer where appears the apps list of the current collection\n* New \"Add Card\" action in the options of the toolbar on the collections screen\n* Improved the exit animation on collection screen\n* Bugs fixed in the top bar on the launcher and others minor bugs fixed too\n\n## 2016/10/25 - Version Name: 2.0.1-alpha - Version Code: 58\n\n* New categorization: bugs fixed, new API models and improves the order\n* The moments change automatically when the wifi is changed, plug in headphone or you drive your car\n* New Moments Out and About and Sport\n* You can add, edit or remove your moments\n* You can pin and unpin your moment from the top bar\n* You can drag applications from dock in order to reorder apps in the dock or remove\n* New preferences for show/hide icons in the moment top bar\n* New subscription screen design for fixing problem with Android 4.4 or lower\n* Added two new developer options to:\n    * Change backend URL at runtime.\n    * Enable/disable Stetho.\n* Refactorized all persistence models.\n* Refactorized all api services models.\n\n## 2016/10/14 - Version Name: 2.0-alpha - Version Code: 57\n\n* First alpha version\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "\n## How Can I Contribute?\n\nThe issues have been integrated into all stages of the development process. This way, the work is coordinated through the so-called Agile Management following Scrum techniques. For this process, we used Projects in GitHub.\n\nFirst, you should create a new issue with the bug or new behaviour that you want to implement in 9 Cards. You also can contribute implementing the [existing issues in 9 Cards](https://github.com/47deg/nine-cards-v2/issues).  \n\nWhen you create a new issue you have to add [the labels](https://github.com/47deg/nine-cards-v2/labels) so other developers can understand the problem or new behaviour.\n\nThe mandatory labels are:\n\n- **Story Points:** Rate the relative work effort in a Fibonacci-like format: 1, 2, 3, 5, or 8. If we are referring to time, the correspondence for every SP is: 2 hours, 1 day, 2 or 3 days, 1 week and 2 weeks. If you want to put 8 SP on one issue, you should consider dividing the issue.\n- **Server or Client:** You should add a new label if the issue is for the [server](https://github.com/47deg/nine-cards-backend) or [client](https://github.com/47deg/nine-cards-v2). In addition, if it's a client issue, you can add a `ui` label if you're only working on UI.\n- **Expertise Level:** Add the label for `beginner`, `intermediate` or `advanced`.\n \nYou have more labels that you can use if you think they're relevent for other developers such as `bug`, `critical`, `test`, and so on.\n \nWhen you have selected the issue that you want to work on, you must add the issue in [the board](https://github.com/47deg/nine-cards-v2/projects) (Server or Client) in the `In progess` column. After that, you should create a new `branch` where you'll implement the code. The name of the branch is important:\n\n- [Github Name]-[Issue Number]-[Small Description]\n\nFor example, `47dev-1213-Fixing_Tests`\n\nEvery issue passes through four statuses:\n\n- **Development:** you are resolving the issue. The issue is in `In Progress` column.\n- **Code review:** another person is reviewing the style of the code. You can assign the issue to another developer. The issue is in the `Code review` column. You need a `LGTM!` or `Thumbs up` to pass on to the next step.\n- **QA:** another person verifies that the code resolves the issue. The issue is in `QA` column. If the branch covers the description as expected, you can pass on to the next step.\n- **Ready to Master:** The issue is in the `Ready to Master` column. You have to wait until we include the code in master.\n\nIf you finish the process, you'll be a contributor of 9 Cards and we'll be happy to have you!\n"
  },
  {
    "path": "ISSUE_TEMPLATE.md",
    "content": "### Description\n\n[Description of the issue]\n\n### Steps to Reproduce\n\n1. [First Step]\n2. [Second Step]\n3. [and so on...]\n\n**Expected behavior:** [What you expect to happen]\n\n**Actual behavior:** [What actually happens]\n\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2016 47 Degrees, LLC. <http://www.47deg.com>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\nRegarding the use of a piece of code from the cards.nine.utils.SystemBarTintManager.java by readyState Software:\n\nCopyright (C) 2013 readyState Software Ltd\n\nAnd cards.nine.app.ui.components.layouts.SlidingTabLayout.scala by The Android Open Source Project\n\nCopyright (C) 2013 The Android Open Source Project\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "PULL_REQUEST_TEMPLATE.md",
    "content": "# Pull Request Checklist\n\n* [ ] Have you read through the [contributor guidelines](https://github.com/47deg/nine-cards-v2/blob/master/CONTRIBUTING.md)?\n* [ ] Have you added copyright headers to new files?\n* [ ] Have you updated the documentation?\n* [ ] Have you added tests for any changed functionality?\n\n## Fixes\n\nFixes #xxxx\n\n## Purpose\n\nWhat does this PR do?\n\n"
  },
  {
    "path": "README.md",
    "content": "[![Join the conversation on Gitter](https://img.shields.io/gitter/room/47deg/nine-cards-v2.svg)](https://gitter.im/47deg/nine-cards-v2)\n\n# 9-Cards\n\n9 Cards was an open source home launcher for Android, whose code was all written in Scala. It provided features for organizing apps into collections, giving quick and easy access to the apps more frequently used, at will.\n\nSince January 2019, the 9-Cards project is now abandoned, and the App is no longer available in the Google Play store. The source code repo has been left here, for those wishing to study or to re-create it, published under the Apache License.\n\nFor a full list of changes please view:[Changelog](CHANGELOG.md). For instructions for building an using the app, see the [old README](README_old.md). \n\n\n[![Google Play](https://cloud.githubusercontent.com/assets/456025/22688567/25acb99e-ed2d-11e6-87df-cda19849fa84.png)](https://play.google.com/store/apps/details?id=com.fortysevendeg.ninecardslauncher)\n\n<img src=\"https://cloud.githubusercontent.com/assets/456025/22655829/ab55e698-ec91-11e6-8858-ef25df5b5b46.png\" width=\"280\"/> <img src=\"https://cloud.githubusercontent.com/assets/456025/22655909/f87f5e36-ec91-11e6-80b9-5278a45784d5.png\" width=\"280\"/> <img src=\"https://cloud.githubusercontent.com/assets/456025/22655919/01be9cfa-ec92-11e6-869f-300d36bdf238.png\" width=\"280\"/>\n\n<img src=\"https://cloud.githubusercontent.com/assets/456025/22655862/cb1411d0-ec91-11e6-91b4-0236701fcffd.png\" width=\"420\"/> <img src=\"https://cloud.githubusercontent.com/assets/456025/22655875/da8dd448-ec91-11e6-8813-b1a22518b46f.png\" width=\"420\"/>\n\n<img src=\"https://cloud.githubusercontent.com/assets/456025/22655880/e5f6c77c-ec91-11e6-8bf1-670b691fc396.png\" width=\"420\"/> <img src=\"https://cloud.githubusercontent.com/assets/456025/22655895/f0408bbe-ec91-11e6-8650-20b3ef4de3a8.png\" width=\"420\"/>\n\n\n"
  },
  {
    "path": "README_old.md",
    "content": "[![Build Status](https://travis-ci.com/47deg/nine-cards-v2.svg?token=wpV9KDdSpewCkwVHdFCg&branch=master)](https://travis-ci.com/47deg/nine-cards-v2) [![Join the conversation on Gitter](https://img.shields.io/gitter/room/47deg/nine-cards-v2.svg)](https://gitter.im/47deg/nine-cards-v2)\n\n# 9 Cards v2\n\n[![Google Play](https://cloud.githubusercontent.com/assets/456025/22688567/25acb99e-ed2d-11e6-87df-cda19849fa84.png)](https://play.google.com/store/apps/details?id=com.fortysevendeg.ninecardslauncher)\n\n<img src=\"https://cloud.githubusercontent.com/assets/456025/22655829/ab55e698-ec91-11e6-8858-ef25df5b5b46.png\" width=\"280\"/> <img src=\"https://cloud.githubusercontent.com/assets/456025/22655909/f87f5e36-ec91-11e6-80b9-5278a45784d5.png\" width=\"280\"/> <img src=\"https://cloud.githubusercontent.com/assets/456025/22655919/01be9cfa-ec92-11e6-869f-300d36bdf238.png\" width=\"280\"/>\n\n<img src=\"https://cloud.githubusercontent.com/assets/456025/22655862/cb1411d0-ec91-11e6-91b4-0236701fcffd.png\" width=\"420\"/> <img src=\"https://cloud.githubusercontent.com/assets/456025/22655875/da8dd448-ec91-11e6-8813-b1a22518b46f.png\" width=\"420\"/>\n\n<img src=\"https://cloud.githubusercontent.com/assets/456025/22655880/e5f6c77c-ec91-11e6-8bf1-670b691fc396.png\" width=\"420\"/> <img src=\"https://cloud.githubusercontent.com/assets/456025/22655895/f0408bbe-ec91-11e6-8650-20b3ef4de3a8.png\" width=\"420\"/>\n\n9 Cards is an open source home launcher for Android written in Scala. This mobile application does the bulk of the work for you, organizing your apps into collections, giving you quick and easy access to the apps you need most, when you need them. \n\nFor a full list of changes please view:[Changelog](CHANGELOG.md)\n\n# Table of Contents\n1. [Prerequisites](#prerequisites)\n2. [Compile and Run](#compile-and-run)\n3. [Properties File](#properties-file)\n4. [Troubleshooting](#troubleshooting)\n\n## Prerequisites\n\n### SBT\n\n* [Download](http://www.scala-sbt.org/download.html) and install sbt.\n\n### Android SDK\n\n* [Download](https://developer.android.com/studio/index.html#downloads). You only need the command line tools.\n* Set `ANDROID_HOME` environment variable pointing to the root folder.\n\n### Android Device\n\nYou need an Android device and must [enable USB debugging](https://www.google.es/search?q=android+activate+developer+mode&oq=android+active+developer).\n\n### Google Project\n\n9 Cards needs the following Google APIs:\n\n* Google Drive API for storing your devices in the cloud.\n* Google Plus API for authenticating the user requests.\n\nYou need to create a project in the Google API Console with these two APIs enabled.\n\nFor that, you have 2 choices: \n\n* Normal Mode (Recommended): You must create the keys in the Google Developers Console. You only need 10 minutes for that.\n* Easy Mode: We give you the keys and you don't have to create the project in the Google Developers Console.\n\n### Normal Mode: Google Project\n\n1. Go to the [Google Developers Console](https://console.developers.google.com/apis/library?project=_).\n2. From the project drop-down, select a [project](https://support.google.com/cloud/answer/6158853), or create a new one.\n\n#### Google Drive API\n\n1. Enable the Google Drive API service:\n    1. In the sidebar under \"API Manager\", select *Library*.\n    2. In the list of Google APIs, search for the Google Drive API service.\n    3. Select Google Drive API from the results list.\n    4. Press the Enable API button.\n2. In the sidebar under \"API Manager\", select Credentials.\n3. In the Credentials tab, select the *Create credentials* drop-down list, and choose OAuth client ID.\n4. Select *Android* as *Application type*.\n5. Enter a key Name.\n6. [Find your certificate SHA1 fingerprint](https://developers.google.com/android/guides/client-auth) and paste it in the form where requested.\n7. Enter `com.fortysevendeg.ninecardslauncher` in the package name field.\n8. Click on \"Create\".\n\n[More info](https://developers.google.com/drive/android/auth)\n\n#### Google Plus API\n\n1. Enable the Google Plus API service:\n    1. In the sidebar under \"API Manager\", select *Library*.\n    2. In the list of Google APIs, search for the Google+ API service.\n    3. Select Google+ API from the results list.\n    4. Press the Enable API button.\n2. In the sidebar under \"API Manager\", select *Credentials*.\n3. In the Credentials tab, select the *Create credentials* drop-down list, and choose OAuth client ID.\n4. Select *Web application* as *Application type*.\n5. Enter a key Name then select Create.\n6. Then copy the *client ID* of the newly generated credential.\n\n### Easy Mode: Google Project\n\nYou must add the following content to `ninecards.properties` file:\n\n```\n# Backend V2\nbackend.v2.url=https://nine-cards.herokuapp.com\nbackend.v2.clientid=411191100294-sjhinp1i2gkp46u36ii7m16v9hog64nn.apps.googleusercontent.com\n```\n\nand you must launch SBT with the following command:\n\n```\n$ sbt -mem 2048 -Ddebug\n```\n\nAt the end of the compilation, previously for installing on a cellphone, you must put the password of the keystore:\n\n```\nEnter keystore password:\n```\n\nThe password is `android`.\n\nNote: If you plan on working on this project, please consider using the `Default Mode`\n\n\n## Compile and Run\n\nTo compile the project:\n\n* Clone this GitHub project to your computer:\n\n```\n$ git clone git@github.com:47deg/nine-cards-v2.git\n```\n\n* Add a `ninecards.properties` file (See [Add Debug Keys](#properties-file) section).\n\n* You need to set the heap size to at least 2M:\n\n```\n$ sbt -mem 2048\n```\n\n* Verify that your device is attached:\n\n```\n> devices\n```\n\nThe output should look like:\n\n```\n[info]  Serial                 Model            Battery % Android Version\n[info]  ---------------------- ---------------- --------- ---------------\n[info]  XXXXXXXXXX             Nexus 6                66% 6.0.1  API 23\n```\n\n* Now you're ready to run the project, just execute:\n\n```\n> run\n```\n\n## Properties File\n\nYou need to add a `ninecards.properties` file in the project root folder. \n\nThis file provides some keys for different third party services. We'll see all these down below.\n\nTo begin with, you can use the template provided in the root folder:\n\n```\n$ cp ninecards.properties.default ninecards.properties\n```\n\nThis is the content:\n\n```\n# Backend V2\nbackend.v2.url=\nbackend.v2.clientid=\n\n# Third Parties\ncrashlytics.enabled=false\ncrashlytics.apikey=\ncrashlytics.apisecret=\nstrictmode.enabled=false\nanalytics.enabled=false\nanalytics.trackid=\n\n# Firebase\nfirebase.enabled=false\nfirebase.url=\nfirebase.google.appid=\nfirebase.google.apikey=\nfirebase.gcm.senderid=\nfirebase.clientid=\n\n# FlowUp\nflowup.enabled=false\nflowup.apikey=\n```\n\n### Backend V2 (Mandatory)\n\n* `backend.v2.url`: Defines the URL for the Backend. Visit the [GitHub project](https://github.com/47deg/nine-cards-backend) for more information.\n* `backend.v2.clientid`: This value is used for requesting a token id that will be used by the Backend to authenticate the user. It's the *client id* obtained in the [Google Plus API section](#google-plus-api). \n\n### Third Parties (Optional)\n\n**[Crashlytics](https://try.crashlytics.com/)**\n\n* `crashlytics.enabled`: Enables or disables the Crashlytics service.\n* `crashlytics.apikey` & `crashlytics.apisecret`: These values are fetched from your [Crashlytics organization page](https://www.fabric.io/settings/organizations).\n\n**[Strict Mode](https://developer.android.com/reference/android/os/StrictMode.html)**\n\n* `strictmode.enabled`: Enables or disables the Strict Mode.\n\n**[Google Analytics](https://developers.google.com/analytics/)**\n\n* `analytics.enabled`: Enables or disables the Google Analytics service.\n* `analytics.trackid`: You can use your own tracking ID. See how to [find your tracking code, tracking ID, and property number](https://support.google.com/analytics/answer/1032385).\n\n**[FlowUp](http://flowup.io)**\n\n* `flowup.enabled`: Enables or disables the FlowUp service.\n* `flowup.apikey`: These values are fetched from your [FlowUp account](http://flowup.io).\n\n### Google Firebase (Optional)\n\nGoogle Firebase is used for push notifications.\n\n**[Google Firebase](https://firebase.google.com/)**\n\n1. Create a Firebase project in the [Firebase console](https://firebase.google.com/console/), if you don't already have one. If you already have an existing Google project associated with your mobile app, click Import Google Project. Otherwise, click Create New Project.\n2. Add a new app in *Project Settings* -> *General*\n3. Select the newly created app and download the `google-services.json`\n4. Open the file in a text editor. All bellow properties are taken from this file: \n\n* `firebase.enabled`: Enables or disables the Google Firebase service\n* `firebase.url`: Property `project_info.firebase_url`\n* `firebase.google.appid`: Property `client[0].client_info.mobilesdk_app_id`\n* `firebase.google.apikey`: Property `client[0].api_key[0].current_key`\n* `firebase.gcm.senderid`: Property `project_info.project_number`\n* `firebase.clientid`: Property `client[0].oauth_client[x].client_id` where x is the index of one element with `client_type` == 3\n\n## Troubleshooting\n\nThis section contains information about possible problems that may occur compiling 9 Cards.\n\n### Ubuntu: ProcessException\n\nWhen you compile the project, it's possible that you will have this error:\n\n`com.android.ide.common.process.ProcessException`\n\nIt's a problem in the 64-bit system and you need to install the `ia32-libs`. You should install the following next:\n\n`sudo apt-get install lib32stdc++6 lib32z1`\n\nMore information can be found [here](http://stackoverflow.com/questions/22701405/aapt-ioexception-error-2-no-such-file-or-directory-why-cant-i-build-my-grad).\n\n### Ubuntu: Launching IntelliJ from unity panel\n\nIf you are using IntelliJ from unity panel it's possible that the app isn't finding the `ANDROID_HOME` environment variable.\n\nUnity launcher doesn't source the user's environment from `.bashrc` and you should include the `ANDROID_HOME` in `/etc/environment` and IntelliJ will work fine.\n"
  },
  {
    "path": "codecov.yml",
    "content": "codecov:\n  bot: 47degdev\ncomment:\n  layout: header, changes, diff\ncoverage:\n  ignore:\n    - modules/commons-tests/*\n    - modules/mock-android/*\n  status:\n    patch: false"
  },
  {
    "path": "modules/api/build.sbt",
    "content": "platformTarget in Android := \"android-24\"\n"
  },
  {
    "path": "modules/api/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"cards.nine.api\"\n          android:versionCode=\"1\"\n          android:versionName=\"1.0\">\n\n    <uses-sdk\n        android:minSdkVersion=\"16\"\n        android:targetSdkVersion=\"24\"/>\n\n    <application/>\n</manifest>\n"
  },
  {
    "path": "modules/api/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Multi-module Sample</string>\n\n</resources>"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/rest/client/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.rest.client\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class ServiceClientException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause map initCause\n}\n\ntrait ImplicitsServiceClientExceptions {\n\n  implicit def accountsServicesExceptionConverter =\n    (t: Throwable) => ServiceClientException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/rest/client/ServiceClient.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.rest.client\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.api.rest.client.http.{HttpClient, HttpClientResponse}\nimport cards.nine.api.rest.client.messages.ServiceClientResponse\nimport monix.eval.Task\nimport play.api.libs.json.{Json, Reads, Writes}\n\nimport scala.util.{Failure, Success, Try}\n\nclass ServiceClient(httpClient: HttpClient, val baseUrl: String)\n    extends ImplicitsServiceClientExceptions {\n\n  def get[Res](\n      path: String,\n      headers: Seq[(String, String)] = Seq.empty,\n      reads: Option[Reads[Res]] = None,\n      emptyResponse: Boolean = false\n  ): TaskService[ServiceClientResponse[Res]] =\n    for {\n      clientResponse <- httpClient\n        .doGet(baseUrl.concat(path), headers)\n        .resolve[ServiceClientException]\n      response <- readResponse(clientResponse, reads, emptyResponse)\n    } yield ServiceClientResponse(clientResponse.statusCode.intValue, response)\n\n  def emptyPost[Res](\n      path: String,\n      headers: Seq[(String, String)] = Seq.empty,\n      reads: Option[Reads[Res]] = None,\n      emptyResponse: Boolean = false\n  ): TaskService[ServiceClientResponse[Res]] =\n    for {\n      clientResponse <- httpClient\n        .doPost(baseUrl.concat(path), headers)\n        .resolve[ServiceClientException]\n      response <- readResponse(clientResponse, reads, emptyResponse)\n    } yield ServiceClientResponse(clientResponse.statusCode, response)\n\n  def post[Req, Res](\n      path: String,\n      headers: Seq[(String, String)] = Seq.empty,\n      body: Req,\n      reads: Option[Reads[Res]] = None,\n      emptyResponse: Boolean = false\n  )(implicit writes: Writes[Req]): TaskService[ServiceClientResponse[Res]] =\n    for {\n      clientResponse <- httpClient\n        .doPost[Req](baseUrl.concat(path), headers, body)\n        .resolve[ServiceClientException]\n      response <- readResponse(clientResponse, reads, emptyResponse)\n    } yield ServiceClientResponse(clientResponse.statusCode, response)\n\n  def emptyPut[Res](\n      path: String,\n      headers: Seq[(String, String)] = Seq.empty,\n      reads: Option[Reads[Res]] = None,\n      emptyResponse: Boolean = false\n  ): TaskService[ServiceClientResponse[Res]] =\n    for {\n      clientResponse <- httpClient\n        .doPut(baseUrl.concat(path), headers)\n        .resolve[ServiceClientException]\n      response <- readResponse(clientResponse, reads, emptyResponse)\n    } yield ServiceClientResponse(clientResponse.statusCode, response)\n\n  def put[Req, Res](\n      path: String,\n      headers: Seq[(String, String)] = Seq.empty,\n      body: Req,\n      reads: Option[Reads[Res]] = None,\n      emptyResponse: Boolean = false\n  )(implicit writes: Writes[Req]): TaskService[ServiceClientResponse[Res]] =\n    for {\n      httpResponse <- httpClient\n        .doPut[Req](baseUrl.concat(path), headers, body)\n        .resolve[ServiceClientException]\n      response <- readResponse(httpResponse, reads, emptyResponse)\n    } yield ServiceClientResponse(httpResponse.statusCode, response)\n\n  def delete[Res](\n      path: String,\n      headers: Seq[(String, String)] = Seq.empty,\n      reads: Option[Reads[Res]] = None,\n      emptyResponse: Boolean = false\n  ): TaskService[ServiceClientResponse[Res]] =\n    for {\n      clientResponse <- httpClient\n        .doDelete(baseUrl.concat(path), headers)\n        .resolve[ServiceClientException]\n      response <- readResponse(clientResponse, reads, emptyResponse)\n    } yield ServiceClientResponse(clientResponse.statusCode, response)\n\n  private def readResponse[T](\n      clientResponse: HttpClientResponse,\n      maybeReads: Option[Reads[T]],\n      emptyResponse: Boolean\n  ): TaskService[Option[T]] = {\n\n    def isError: Boolean =\n      clientResponse.statusCode >= 400 && clientResponse.statusCode < 600\n\n    TaskService {\n      Task {\n        if (isError) {\n          Left(\n            ServiceClientException(\n              s\"Error making request. Status code ${clientResponse.statusCode}\"))\n        } else {\n          (clientResponse.body, emptyResponse, maybeReads) match {\n            case (Some(d), false, Some(r)) => transformResponse[T](d, r)\n            case (None, false, _)          => Left(ServiceClientException(\"No content\"))\n            case (Some(d), false, None) =>\n              Left(ServiceClientException(\"No transformer found for type\"))\n            case _ => Right(None)\n          }\n        }\n      }\n    }\n  }\n\n  private def transformResponse[T](\n      content: String,\n      reads: Reads[T]\n  ): Either[ServiceClientException, Some[T]] =\n    Try(Json.parse(content).as[T](reads)) match {\n      case Success(s) => Right(Some(s))\n      case Failure(e) => Left(ServiceClientException(message = e.getMessage, cause = Some(e)))\n    }\n\n}\n"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/rest/client/http/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.rest.client.http\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class HttpClientException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsHttpClientExceptions {\n  implicit def httpClientExceptionConverter =\n    (t: Throwable) => HttpClientException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/rest/client/http/HttpClient.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.rest.client.http\n\nimport cards.nine.commons.services.TaskService.TaskService\nimport play.api.libs.json.Writes\n\ntrait HttpClient {\n\n  def doGet(\n      url: String,\n      httpHeaders: Seq[(String, String)]\n  ): TaskService[HttpClientResponse]\n\n  def doDelete(\n      url: String,\n      httpHeaders: Seq[(String, String)]\n  ): TaskService[HttpClientResponse]\n\n  def doPost(\n      url: String,\n      httpHeaders: Seq[(String, String)]\n  ): TaskService[HttpClientResponse]\n\n  def doPost[Req: Writes](\n      url: String,\n      httpHeaders: Seq[(String, String)],\n      body: Req\n  ): TaskService[HttpClientResponse]\n\n  def doPut(\n      url: String,\n      httpHeaders: Seq[(String, String)]\n  ): TaskService[HttpClientResponse]\n\n  def doPut[Req: Writes](\n      url: String,\n      httpHeaders: Seq[(String, String)],\n      body: Req\n  ): TaskService[HttpClientResponse]\n\n}\n"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/rest/client/http/HttpClientResponse.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.rest.client.http\n\ncase class HttpClientResponse(statusCode: Int, body: Option[String])\n"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/rest/client/http/OkHttpClient.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.rest.client.http\n\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.api.rest.client.http.Methods._\nimport play.api.libs.json.{Json, Writes}\nimport scala.collection.JavaConverters._\n\nclass OkHttpClient(okHttpClient: okhttp3.OkHttpClient = new okhttp3.OkHttpClient)\n    extends HttpClient\n    with ImplicitsHttpClientExceptions {\n\n  val jsonMediaType = okhttp3.MediaType.parse(\"application/json\")\n\n  val textPlainMediaType = okhttp3.MediaType.parse(\"text/plain\")\n\n  override def doGet(\n      url: String,\n      httpHeaders: Seq[(String, String)]\n  ): TaskService[HttpClientResponse] =\n    doMethod(GET, url, httpHeaders)\n\n  override def doDelete(\n      url: String,\n      httpHeaders: Seq[(String, String)]\n  ): TaskService[HttpClientResponse] =\n    doMethod(DELETE, url, httpHeaders)\n\n  override def doPost(\n      url: String,\n      httpHeaders: Seq[(String, String)]\n  ): TaskService[HttpClientResponse] =\n    doMethod(POST, url, httpHeaders)\n\n  override def doPost[Req: Writes](\n      url: String,\n      httpHeaders: Seq[(String, String)],\n      body: Req\n  ): TaskService[HttpClientResponse] =\n    doMethod(POST, url, httpHeaders, Some(Json.toJson(body).toString()))\n\n  override def doPut(\n      url: String,\n      httpHeaders: Seq[(String, String)]\n  ): TaskService[HttpClientResponse] =\n    doMethod(PUT, url, httpHeaders)\n\n  override def doPut[Req: Writes](\n      url: String,\n      httpHeaders: Seq[(String, String)],\n      body: Req\n  ): TaskService[HttpClientResponse] =\n    doMethod(PUT, url, httpHeaders, Some(Json.toJson(body).toString()))\n\n  private[this] def doMethod[T](\n      method: Method,\n      url: String,\n      httpHeaders: Seq[(String, String)],\n      body: Option[String] = None,\n      responseHandler: okhttp3.Response => T = defaultResponseHandler _): TaskService[T] =\n    TaskService {\n      CatchAll[HttpClientException] {\n        val builder = createBuilderRequest(url, httpHeaders)\n        val request = (method match {\n          case GET    => builder.get()\n          case DELETE => builder.delete()\n          case POST   => builder.post(createBody(body))\n          case PUT    => builder.put(createBody(body))\n        }).build()\n        responseHandler(okHttpClient.newCall(request).execute())\n      }\n    }\n\n  private[this] def defaultResponseHandler(response: okhttp3.Response): HttpClientResponse =\n    HttpClientResponse(response.code(), Option(response.body()) map (_.string()))\n\n  private[this] def createBuilderRequest(\n      url: String,\n      httpHeaders: Seq[(String, String)]): okhttp3.Request.Builder =\n    new okhttp3.Request.Builder().url(url).headers(createHeaders(httpHeaders))\n\n  private[this] def createHeaders(httpHeaders: Seq[(String, String)]): okhttp3.Headers =\n    okhttp3.Headers.of(httpHeaders.map {\n      case (key, value) => key -> value\n    }.toMap.asJava)\n\n  private[this] def createBody(body: Option[String]) =\n    body match {\n      case Some(b) => okhttp3.RequestBody.create(jsonMediaType, b)\n      case _       => okhttp3.RequestBody.create(textPlainMediaType, \"\")\n    }\n\n}\n\nobject Methods {\n\n  sealed trait Method\n\n  case object GET extends Method\n\n  case object DELETE extends Method\n\n  case object POST extends Method\n\n  case object PUT extends Method\n\n}\n"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/rest/client/messages/Messages.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.rest.client.messages\n\ncase class ServiceClientResponse[T](statusCode: Int, data: Option[T])\n\nclass ServiceClientException(message: String) extends RuntimeException(message)\n"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/version1/ApiService.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.version1\n\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.api.rest.client.ServiceClient\nimport cards.nine.api.rest.client.messages.ServiceClientResponse\nimport play.api.libs.json.{Reads, Writes}\n\nclass ApiService(serviceClient: ServiceClient) {\n\n  val prefixPathUser = \"/users\"\n\n  val prefixPathUserConfig = \"/ninecards/userconfig\"\n\n  def baseUrl: String = serviceClient.baseUrl\n\n  def login(user: User, headers: Seq[(String, String)])(\n      implicit reads: Reads[User],\n      writes: Writes[User]): TaskService[ServiceClientResponse[User]] =\n    serviceClient\n      .post[User, User](path = prefixPathUser, headers = headers, body = user, reads = Some(reads))\n\n  def getUserConfig(\n      headers: Seq[(String, String)]\n  )(implicit reads: Reads[UserConfig]): TaskService[ServiceClientResponse[UserConfig]] =\n    serviceClient\n      .get[UserConfig](path = prefixPathUserConfig, headers = headers, reads = Some(reads))\n\n}\n"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/version1/JsonImplicits.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.version1\n\nobject JsonImplicits {\n\n  import play.api.libs.json._\n\n  implicit val authAnonymousReads            = Json.reads[AuthAnonymous]\n  implicit val authFacebookReads             = Json.reads[AuthFacebook]\n  implicit val authTwitterReads              = Json.reads[AuthTwitter]\n  implicit val authGoogleDeviceReads         = Json.reads[AuthGoogleDevice]\n  implicit val authGoogleReads               = Json.reads[AuthGoogle]\n  implicit val authDataReads                 = Json.reads[AuthData]\n  implicit val userReads                     = Json.reads[User]\n  implicit val userConfigTimeSlotReads       = Json.reads[UserConfigTimeSlot]\n  implicit val userConfigUserLocationReads   = Json.reads[UserConfigUserLocation]\n  implicit val userConfigCollectionItemReads = Json.reads[UserConfigCollectionItem]\n  implicit val userConfigCollectionReads     = Json.reads[UserConfigCollection]\n  implicit val userConfigProfileImageReads   = Json.reads[UserConfigProfileImage]\n  implicit val userConfigStatusInfoReads     = Json.reads[UserConfigStatusInfo]\n  implicit val userConfigGeoInfoReads        = Json.reads[UserConfigGeoInfo]\n  implicit val userConfigDeviceReads         = Json.reads[UserConfigDevice]\n  implicit val userConfigPlusProfileReads    = Json.reads[UserConfigPlusProfile]\n  implicit val userConfigReads               = Json.reads[UserConfig]\n\n  implicit val authAnonymousWrites            = Json.writes[AuthAnonymous]\n  implicit val authFacebookWrites             = Json.writes[AuthFacebook]\n  implicit val authTwitterWrites              = Json.writes[AuthTwitter]\n  implicit val authGoogleDeviceWrites         = Json.writes[AuthGoogleDevice]\n  implicit val authGoogleWrites               = Json.writes[AuthGoogle]\n  implicit val authDataWrites                 = Json.writes[AuthData]\n  implicit val userWrites                     = Json.writes[User]\n  implicit val userConfigTimeSlotWrites       = Json.writes[UserConfigTimeSlot]\n  implicit val userConfigUserLocationWrites   = Json.writes[UserConfigUserLocation]\n  implicit val userConfigCollectionItemWrites = Json.writes[UserConfigCollectionItem]\n  implicit val userConfigCollectionWrites     = Json.writes[UserConfigCollection]\n  implicit val userConfigProfileImageWrites   = Json.writes[UserConfigProfileImage]\n  implicit val userConfigStatusInfoWrites     = Json.writes[UserConfigStatusInfo]\n  implicit val userConfigGeoInfoWrites        = Json.writes[UserConfigGeoInfo]\n  implicit val userConfigDeviceWrites         = Json.writes[UserConfigDevice]\n  implicit val userConfigPlusProfileWrites    = Json.writes[UserConfigPlusProfile]\n  implicit val userConfigWrites               = Json.writes[UserConfig]\n\n}\n"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/version1/Model.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.version1\n\nimport play.api.libs.json._\n\ncase class User(\n    _id: Option[String],\n    sessionToken: Option[String],\n    username: Option[String],\n    password: Option[String],\n    email: Option[String],\n    authData: Option[AuthData])\n\ncase class AuthData(\n    google: Option[AuthGoogle],\n    facebook: Option[AuthFacebook],\n    twitter: Option[AuthTwitter],\n    anonymous: Option[AuthAnonymous])\n\ncase class AuthGoogleDevice(\n    name: String,\n    deviceId: String,\n    secretToken: String,\n    permissions: Seq[String])\n\ncase class AuthGoogle(email: String, devices: Seq[AuthGoogleDevice])\n\ncase class AuthTwitter(\n    id: String,\n    screenName: String,\n    consumerKey: String,\n    consumerSecret: String,\n    authToken: String,\n    authTokenSecret: String,\n    key: String,\n    secretKey: String)\n\ncase class AuthFacebook(id: String, accessToken: String, expirationDate: Long)\n\ncase class AuthAnonymous(id: String)\n\ncase class UserConfig(\n    _id: String,\n    email: String,\n    plusProfile: UserConfigPlusProfile,\n    devices: Seq[UserConfigDevice],\n    geoInfo: UserConfigGeoInfo,\n    status: UserConfigStatusInfo)\n\ncase class UserConfigPlusProfile(displayName: String, profileImage: UserConfigProfileImage)\n\ncase class UserConfigDevice(\n    deviceId: String,\n    deviceName: String,\n    collections: Seq[UserConfigCollection])\n\ncase class UserConfigGeoInfo(\n    homeMorning: Option[UserConfigUserLocation],\n    homeNight: Option[UserConfigUserLocation],\n    work: Option[UserConfigUserLocation],\n    current: Option[UserConfigUserLocation])\n\ncase class UserConfigStatusInfo(\n    products: Seq[String],\n    friendsReferred: Int,\n    themesShared: Int,\n    collectionsShared: Int,\n    customCollections: Int,\n    earlyAdopter: Boolean,\n    communityMember: Boolean,\n    joinedThrough: Option[String],\n    tester: Boolean)\n\ncase class UserConfigProfileImage(imageType: Int, imageUrl: String, secureUrl: Option[String])\n\ncase class UserConfigCollection(\n    name: String,\n    originalSharedCollectionId: Option[String],\n    sharedCollectionId: Option[String],\n    sharedCollectionSubscribed: Option[Boolean],\n    items: Seq[UserConfigCollectionItem],\n    collectionType: String,\n    constrains: Seq[String],\n    wifi: Seq[String],\n    occurrence: Seq[String],\n    icon: String,\n    radius: Int,\n    lat: Double,\n    lng: Double,\n    alt: Double,\n    category: Option[String])\n\ncase class UserConfigCollectionItem(\n    itemType: String,\n    title: String,\n    metadata: JsValue,\n    categories: Option[Seq[String]])\n\ncase class UserConfigUserLocation(\n    wifi: String,\n    lat: Double,\n    lng: Double,\n    occurrence: Seq[UserConfigTimeSlot])\n\ncase class UserConfigTimeSlot(from: String, to: String, days: Seq[Int])\n"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/version2/ApiService.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.version2\n\nimport javax.crypto.Mac\nimport javax.crypto.spec.SecretKeySpec\n\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.api.rest.client.http.HttpClientException\nimport cards.nine.api.rest.client.messages.ServiceClientResponse\nimport cards.nine.api.rest.client.{ServiceClient, ServiceClientException}\nimport play.api.libs.json.{Reads, Writes}\n\nclass ApiService(serviceClient: ServiceClient) {\n\n  def baseUrl: String = serviceClient.baseUrl\n\n  type ApiException = HttpClientException with ServiceClientException\n\n  private[this] val headerContentType = \"Content-Type\"\n\n  private[this] val headerContentTypeValue = \"application/json\"\n\n  private[this] val headerAuthToken = \"X-Auth-Token\"\n\n  private[this] val headerSessionToken = \"X-Session-Token\"\n\n  private[this] val headerAndroidId = \"X-Android-ID\"\n\n  private[this] val headerMarketLocalization = \"X-Android-Market-Localization\"\n\n  private[this] val headerMarketLocalizationValue = \"en-US\"\n\n  private[this] val headerAndroidMarketToken = \"X-Google-Play-Token\"\n\n  private[this] val loginPath = \"/login\"\n\n  private[this] val installationsPath = \"/installations\"\n\n  private[this] val collectionsPath = \"/collections\"\n\n  private[this] val subscriptionsPath = s\"$collectionsPath/subscriptions\"\n\n  private[this] val latestCollectionsPath = s\"$collectionsPath/latest\"\n\n  private[this] val topCollectionsPath = s\"$collectionsPath/top\"\n\n  private[this] val viewsPath = s\"/views\"\n\n  private[this] val applicationsPath = \"/applications\"\n\n  private[this] val categorizePath = s\"$applicationsPath/categorize\"\n\n  private[this] val rankPath = s\"$applicationsPath/rank\"\n\n  private[this] val rankAppsByMomentPath = s\"$applicationsPath/rank-by-moments\"\n\n  private[this] val rankWidgetsByMomentPath = \"/widgets/rank\"\n\n  private[this] val categorizeDetailPath = s\"$applicationsPath/details\"\n\n  private[this] val recommendationsPath = \"/recommendations\"\n\n  private[this] val searchPath = s\"$applicationsPath/search\"\n\n  def login(request: ApiLoginRequest)(\n      implicit reads: Reads[ApiLoginResponse],\n      writes: Writes[ApiLoginRequest]): TaskService[ServiceClientResponse[ApiLoginResponse]] =\n    serviceClient.post[ApiLoginRequest, ApiLoginResponse](\n      path = loginPath,\n      headers = Seq((headerContentType, headerContentTypeValue)),\n      body = request,\n      reads = Some(reads))\n\n  def installations(request: InstallationRequest, header: ServiceHeader)(\n      implicit reads: Reads[InstallationResponse],\n      writes: Writes[InstallationRequest]): TaskService[\n    ServiceClientResponse[InstallationResponse]] =\n    serviceClient.put[InstallationRequest, InstallationResponse](\n      path = installationsPath,\n      headers = createHeaders(installationsPath, header),\n      body = request,\n      reads = Some(reads))\n\n  def latestCollections(category: String, offset: Int, limit: Int, header: ServiceMarketHeader)(\n      implicit reads: Reads[CollectionsResponse]): TaskService[\n    ServiceClientResponse[CollectionsResponse]] = {\n\n    val path = s\"$latestCollectionsPath/$category/$offset/$limit\"\n\n    serviceClient.get[CollectionsResponse](\n      path = path,\n      headers = createHeaders(path, header),\n      reads = Some(reads))\n  }\n\n  def topCollections(category: String, offset: Int, limit: Int, header: ServiceMarketHeader)(\n      implicit reads: Reads[CollectionsResponse]): TaskService[\n    ServiceClientResponse[CollectionsResponse]] = {\n\n    val path = s\"$topCollectionsPath/$category/$offset/$limit\"\n\n    serviceClient.get[CollectionsResponse](\n      path = path,\n      headers = createHeaders(path, header),\n      reads = Some(reads))\n  }\n\n  def createCollection(request: CreateCollectionRequest, header: ServiceHeader)(\n      implicit reads: Reads[CreateCollectionResponse],\n      writes: Writes[CreateCollectionRequest]): TaskService[\n    ServiceClientResponse[CreateCollectionResponse]] =\n    serviceClient.post[CreateCollectionRequest, CreateCollectionResponse](\n      path = collectionsPath,\n      headers = createHeaders(collectionsPath, header),\n      body = request,\n      reads = Some(reads))\n\n  def updateCollection(\n      publicIdentifier: String,\n      request: UpdateCollectionRequest,\n      header: ServiceHeader)(\n      implicit reads: Reads[UpdateCollectionResponse],\n      writes: Writes[UpdateCollectionRequest]): TaskService[\n    ServiceClientResponse[UpdateCollectionResponse]] = {\n\n    val path = s\"$collectionsPath/$publicIdentifier\"\n\n    serviceClient.put[UpdateCollectionRequest, UpdateCollectionResponse](\n      path = path,\n      headers = createHeaders(path, header),\n      body = request,\n      reads = Some(reads))\n  }\n\n  def getCollection(publicIdentifier: String, header: ServiceMarketHeader)(\n      implicit reads: Reads[Collection]): TaskService[ServiceClientResponse[Collection]] = {\n\n    val path = s\"$collectionsPath/$publicIdentifier\"\n\n    serviceClient\n      .get[Collection](path = path, headers = createHeaders(path, header), reads = Some(reads))\n  }\n\n  def getCollections(header: ServiceMarketHeader)(\n      implicit reads: Reads[CollectionsResponse]): TaskService[\n    ServiceClientResponse[CollectionsResponse]] =\n    serviceClient.get[CollectionsResponse](\n      path = collectionsPath,\n      headers = createHeaders(collectionsPath, header),\n      reads = Some(reads))\n\n  def categorize(request: CategorizeRequest, header: ServiceMarketHeader)(\n      implicit reads: Reads[CategorizeResponse],\n      writes: Writes[CategorizeRequest]): TaskService[ServiceClientResponse[CategorizeResponse]] =\n    serviceClient.post[CategorizeRequest, CategorizeResponse](\n      path = categorizePath,\n      headers = createHeaders(categorizePath, header),\n      body = request,\n      reads = Some(reads))\n\n  def categorizeDetail(request: CategorizeRequest, header: ServiceMarketHeader)(\n      implicit reads: Reads[CategorizeDetailResponse],\n      writes: Writes[CategorizeRequest]): TaskService[\n    ServiceClientResponse[CategorizeDetailResponse]] =\n    serviceClient.post[CategorizeRequest, CategorizeDetailResponse](\n      path = categorizeDetailPath,\n      headers = createHeaders(categorizeDetailPath, header),\n      body = request,\n      reads = Some(reads))\n\n  def recommendations(\n      category: String,\n      filter: Option[String],\n      request: RecommendationsRequest,\n      header: ServiceMarketHeader)(\n      implicit reads: Reads[RecommendationsResponse],\n      writes: Writes[RecommendationsRequest]): TaskService[\n    ServiceClientResponse[RecommendationsResponse]] = {\n\n    val path = filter match {\n      case Some(f) => s\"$recommendationsPath/$category/$f\"\n      case _       => s\"$recommendationsPath/$category\"\n    }\n\n    serviceClient.post[RecommendationsRequest, RecommendationsResponse](\n      path = path,\n      headers = createHeaders(path, header),\n      body = request,\n      reads = Some(reads))\n  }\n\n  def recommendationsByApps(request: RecommendationsByAppsRequest, header: ServiceMarketHeader)(\n      implicit reads: Reads[RecommendationsByAppsResponse],\n      writes: Writes[RecommendationsByAppsRequest]): TaskService[\n    ServiceClientResponse[RecommendationsByAppsResponse]] =\n    serviceClient.post[RecommendationsByAppsRequest, RecommendationsByAppsResponse](\n      path = recommendationsPath,\n      headers = createHeaders(recommendationsPath, header),\n      body = request,\n      reads = Some(reads))\n\n  def getSubscriptions(header: ServiceHeader)(\n      implicit reads: Reads[SubscriptionsResponse]): TaskService[\n    ServiceClientResponse[SubscriptionsResponse]] =\n    serviceClient.get[SubscriptionsResponse](\n      path = subscriptionsPath,\n      headers = createHeaders(subscriptionsPath, header),\n      reads = Some(reads))\n\n  def subscribe(\n      publicIdentifier: String,\n      header: ServiceHeader): TaskService[ServiceClientResponse[Unit]] = {\n\n    val path = s\"$subscriptionsPath/$publicIdentifier\"\n\n    serviceClient.emptyPut(\n      path = path,\n      headers = createHeaders(path, header),\n      reads = None,\n      emptyResponse = true)\n  }\n\n  def unsubscribe(\n      publicIdentifier: String,\n      header: ServiceHeader): TaskService[ServiceClientResponse[Unit]] = {\n\n    val path = s\"$subscriptionsPath/$publicIdentifier\"\n\n    serviceClient.delete(\n      path = path,\n      headers = createHeaders(path, header),\n      reads = None,\n      emptyResponse = true)\n  }\n\n  def updateViewShareCollection(\n      publicIdentifier: String,\n      header: ServiceHeader): TaskService[ServiceClientResponse[Unit]] = {\n\n    val path = s\"$collectionsPath/$publicIdentifier$viewsPath\"\n\n    serviceClient.emptyPost(\n      path = path,\n      headers = createHeaders(path, header),\n      reads = None,\n      emptyResponse = true\n    )\n  }\n\n  def rankApps(request: RankAppsRequest, header: ServiceHeader)(\n      implicit reads: Reads[RankAppsResponse],\n      writes: Writes[RankAppsRequest]): TaskService[ServiceClientResponse[RankAppsResponse]] =\n    serviceClient.post[RankAppsRequest, RankAppsResponse](\n      path = rankPath,\n      headers = createHeaders(rankPath, header),\n      body = request,\n      reads = Some(reads))\n\n  def rankAppsByMoment(request: RankAppsByMomentRequest, header: ServiceHeader)(\n      implicit reads: Reads[RankAppsByMomentResponse],\n      writes: Writes[RankAppsByMomentRequest]): TaskService[\n    ServiceClientResponse[RankAppsByMomentResponse]] =\n    serviceClient.post[RankAppsByMomentRequest, RankAppsByMomentResponse](\n      path = rankAppsByMomentPath,\n      headers = createHeaders(rankAppsByMomentPath, header),\n      body = request,\n      reads = Some(reads))\n\n  def rankWidgetsByMoment(request: RankWidgetsByMomentRequest, header: ServiceHeader)(\n      implicit reads: Reads[RankWidgetsByMomentResponse],\n      writes: Writes[RankWidgetsByMomentRequest]): TaskService[\n    ServiceClientResponse[RankWidgetsByMomentResponse]] =\n    serviceClient.post[RankWidgetsByMomentRequest, RankWidgetsByMomentResponse](\n      path = rankWidgetsByMomentPath,\n      headers = createHeaders(rankWidgetsByMomentPath, header),\n      body = request,\n      reads = Some(reads))\n\n  def search(request: SearchRequest, header: ServiceMarketHeader)(\n      implicit reads: Reads[SearchResponse],\n      writes: Writes[SearchRequest]): TaskService[ServiceClientResponse[SearchResponse]] =\n    serviceClient.post[SearchRequest, SearchResponse](\n      path = searchPath,\n      headers = createHeaders(searchPath, header),\n      body = request,\n      reads = Some(reads))\n\n  private[this] def createHeaders[T <: BaseServiceHeader](\n      path: String,\n      header: T): Seq[(String, String)] = {\n\n    def readAndroidMarketToken: Option[String] = header match {\n      case h: ServiceMarketHeader => h.androidMarketToken\n      case _                      => None\n    }\n\n    val algorithm = \"HmacSHA512\"\n    val charset   = \"UTF-8\"\n\n    def hashMac(apiKey: String, url: String): String = {\n      val mac    = Mac.getInstance(algorithm)\n      val secret = new SecretKeySpec(apiKey.getBytes(charset), algorithm)\n      mac.init(secret)\n      val bytesResult = mac.doFinal(url.getBytes(charset))\n      bytesResult.map(\"%02x\".format(_)).mkString\n    }\n\n    Seq(\n      (headerContentType, headerContentTypeValue),\n      (headerAuthToken, hashMac(header.apiKey, serviceClient.baseUrl.concat(path))),\n      (headerSessionToken, header.sessionToken),\n      (headerAndroidId, header.androidId),\n      (headerMarketLocalization, headerMarketLocalizationValue)) ++\n      (readAndroidMarketToken map ((headerAndroidMarketToken, _))).toSeq\n  }\n\n}\n"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/version2/JsonImplicits.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.version2\n\nobject JsonImplicits {\n\n  import play.api.libs.json._\n\n  implicit val loginResponseReads                 = Json.reads[ApiLoginResponse]\n  implicit val installationResponseReads          = Json.reads[InstallationResponse]\n  implicit val collectionAppReads                 = Json.reads[CollectionApp]\n  implicit val collectionReads                    = Json.reads[Collection]\n  implicit val collectionsResponseReads           = Json.reads[CollectionsResponse]\n  implicit val packagesStatsReads                 = Json.reads[PackagesStats]\n  implicit val createCollectionResponseReads      = Json.reads[CreateCollectionResponse]\n  implicit val updateCollectionResponseReads      = Json.reads[UpdateCollectionResponse]\n  implicit val categorizedAppReads                = Json.reads[CategorizedApp]\n  implicit val categorizedAppDetailReads          = Json.reads[CategorizedAppDetail]\n  implicit val categorizeResponseReads            = Json.reads[CategorizeResponse]\n  implicit val categorizeDetailResponseReads      = Json.reads[CategorizeDetailResponse]\n  implicit val recommendationAppReads             = Json.reads[NotCategorizedApp]\n  implicit val recommendationsResponseReads       = Json.reads[RecommendationsResponse]\n  implicit val recommendationsByAppsResponseReads = Json.reads[RecommendationsByAppsResponse]\n  implicit val subscriptionsResponseReads         = Json.reads[SubscriptionsResponse]\n  implicit val rankAppsCategoryResponseReads      = Json.reads[RankAppsCategoryResponse]\n  implicit val rankAppsResponseReads              = Json.reads[RankAppsResponse]\n  implicit val rankAppsByMomentResponseReads      = Json.reads[RankAppsByMomentResponse]\n  implicit val rankWidgetsResponseReads           = Json.reads[RankWidgetsResponse]\n  implicit val rankWidgetsWithMomentResponse      = Json.reads[RankWidgetsWithMomentResponse]\n  implicit val rankWidgetsByMomentResponse        = Json.reads[RankWidgetsByMomentResponse]\n  implicit val searchResponseReads                = Json.reads[SearchResponse]\n\n  implicit val loginRequestWrites                 = Json.writes[ApiLoginRequest]\n  implicit val installationRequestWrites          = Json.writes[InstallationRequest]\n  implicit val createCollectionRequestWrites      = Json.writes[CreateCollectionRequest]\n  implicit val collectionUpdateInfoWrites         = Json.writes[CollectionUpdateInfo]\n  implicit val updateCollectionRequestWrites      = Json.writes[UpdateCollectionRequest]\n  implicit val categorizeRequestWrites            = Json.writes[CategorizeRequest]\n  implicit val recommendationsRequestWrites       = Json.writes[RecommendationsRequest]\n  implicit val recommendationsByAppsRequestWrites = Json.writes[RecommendationsByAppsRequest]\n  implicit val rankAppsRequestWrites              = Json.writes[RankAppsRequest]\n  implicit val rankAppsByMomentRequestWrites      = Json.writes[RankAppsByMomentRequest]\n  implicit val rankWidgetsByMomentRequest         = Json.writes[RankWidgetsByMomentRequest]\n  implicit val searchRequestWrites                = Json.writes[SearchRequest]\n\n}\n"
  },
  {
    "path": "modules/api/src/main/scala/cards/nine/api/version2/Model.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.version2\n\ntrait BaseServiceHeader {\n\n  def apiKey: String\n  def sessionToken: String\n  def androidId: String\n\n}\n\ncase class ServiceHeader(apiKey: String, sessionToken: String, androidId: String)\n    extends BaseServiceHeader\n\ncase class ServiceMarketHeader(\n    apiKey: String,\n    sessionToken: String,\n    androidId: String,\n    androidMarketToken: Option[String])\n    extends BaseServiceHeader\n\ncase class ApiLoginRequest(email: String, androidId: String, tokenId: String)\n\ncase class ApiLoginResponse(apiKey: String, sessionToken: String)\n\ncase class InstallationRequest(deviceToken: String)\n\ncase class InstallationResponse(androidId: String, deviceToken: String)\n\ncase class CollectionsResponse(collections: Seq[Collection])\n\ncase class CreateCollectionRequest(\n    name: String,\n    author: String,\n    icon: String,\n    category: String,\n    community: Boolean,\n    packages: Seq[String])\n\ncase class CreateCollectionResponse(publicIdentifier: String, packagesStats: PackagesStats)\n\ncase class UpdateCollectionRequest(\n    collectionInfo: Option[CollectionUpdateInfo],\n    packages: Option[Seq[String]])\n\ncase class UpdateCollectionResponse(publicIdentifier: String, packagesStats: PackagesStats)\n\ncase class CategorizeRequest(items: Seq[String])\n\ncase class CategorizeResponse(errors: Seq[String], items: Seq[CategorizedApp])\n\ncase class CategorizeDetailResponse(errors: Seq[String], items: Seq[CategorizedAppDetail])\n\ncase class RecommendationsRequest(excludePackages: Seq[String], limit: Int)\n\ncase class RecommendationsResponse(items: Seq[NotCategorizedApp])\n\ncase class RecommendationsByAppsRequest(\n    packages: Seq[String],\n    excludePackages: Seq[String],\n    limit: Int)\n\ncase class RecommendationsByAppsResponse(apps: Seq[NotCategorizedApp])\n\ncase class SubscriptionsResponse(subscriptions: Seq[String])\n\ncase class RankAppsRequest(items: Map[String, Seq[String]], location: Option[String])\n\ncase class RankAppsResponse(items: Seq[RankAppsCategoryResponse])\n\ncase class RankAppsCategoryResponse(category: String, packages: Seq[String])\n\ncase class SearchRequest(query: String, excludePackages: Seq[String], limit: Int)\n\ncase class SearchResponse(items: Seq[NotCategorizedApp])\n\ncase class RankAppsByMomentRequest(\n    items: Seq[String],\n    moments: Seq[String],\n    location: Option[String],\n    limit: Int)\n\ncase class RankAppsByMomentResponse(items: Seq[RankAppsCategoryResponse])\n\ncase class RankWidgetsResponse(packageName: String, className: String)\n\ncase class RankWidgetsByMomentRequest(\n    items: Seq[String],\n    moments: Seq[String],\n    location: Option[String],\n    limit: Int)\n\ncase class RankWidgetsWithMomentResponse(moment: String, widgets: Seq[RankWidgetsResponse])\n\ncase class RankWidgetsByMomentResponse(items: Seq[RankWidgetsWithMomentResponse])\n\ncase class PackagesStats(added: Int, removed: Option[Int] = None)\n\ncase class Collection(\n    name: String,\n    author: String,\n    owned: Boolean,\n    icon: String,\n    category: String,\n    community: Boolean,\n    publishedOn: String,\n    installations: Option[Int],\n    views: Option[Int],\n    subscriptions: Option[Int],\n    publicIdentifier: String,\n    appsInfo: Seq[CollectionApp],\n    packages: Seq[String])\n\ncase class CollectionApp(\n    stars: Double,\n    icon: String,\n    packageName: String,\n    downloads: String,\n    categories: Seq[String],\n    title: String,\n    free: Boolean)\n\ncase class CollectionUpdateInfo(title: String)\n\ncase class CategorizedApp(packageName: String, categories: Seq[String])\n\ncase class CategorizedAppDetail(\n    packageName: String,\n    title: String,\n    categories: Seq[String],\n    icon: String,\n    free: Boolean,\n    downloads: String,\n    stars: Double)\n\ncase class NotCategorizedApp(\n    packageName: String,\n    title: String,\n    downloads: String,\n    icon: String,\n    stars: Double,\n    free: Boolean,\n    screenshots: Seq[String])\n"
  },
  {
    "path": "modules/api/src/test/scala/cards/nine/api/rest/client/Messages.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.rest.client\n\ncase class SampleRequest(message: String)\n\ncase class SampleResponse(message: String)\n"
  },
  {
    "path": "modules/api/src/test/scala/cards/nine/api/rest/client/ServiceClientData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.rest.client\n\nimport cards.nine.api.rest.client.http.{HttpClientException, HttpClientResponse}\nimport play.api.libs.json.Json\n\ntrait ServiceClientData {\n\n  case class SampleRequest(message: String)\n\n  case class SampleResponse(message: String)\n\n  val baseUrl = \"http://sampleUrl\"\n\n  val path = \"/myPath\"\n\n  val headers = Seq((\"header1\", \"value1\"), (\"header2\", \"value2\"))\n\n  implicit val readsResponse = Json.reads[SampleResponse]\n  implicit val writesRequest = Json.writes[SampleRequest]\n\n  val message = \"Hello World!\"\n\n  val json = s\"\"\"{ \"message\" : \"$message\" }\"\"\"\n\n  val invalidJson = s\"\"\"{ \"unknownProperty\" : false }\"\"\"\n\n  val sampleRequest = SampleRequest(\"sample-request\")\n\n  val sampleResponse = Some(SampleResponse(message))\n\n  val statusCodeOk = 200\n\n  val statusCodeNotFound = 404\n\n  val validHttpClientResponse = HttpClientResponse(statusCodeOk, Some(json))\n\n  val validEmptyHttpClientResponse = HttpClientResponse(statusCodeOk, None)\n\n  val notFoundHttpClientResponse = HttpClientResponse(statusCodeNotFound, None)\n\n  val invalidHttpClientResponse = HttpClientResponse(statusCodeOk, Some(invalidJson))\n\n  val exception = HttpClientException(\"\")\n\n}\n"
  },
  {
    "path": "modules/api/src/test/scala/cards/nine/api/rest/client/ServiceClientSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.rest.client\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.api.rest.client.http.{HttpClient, HttpClientException, HttpClientResponse}\nimport org.hamcrest.core.IsEqual\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\nimport play.api.libs.json.Json\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport monix.eval.Task\nimport cats.syntax.either._\nimport cards.nine.commons.test.TaskServiceSpecification\n\ntrait ServiceClientSpecification\n    extends TaskServiceSpecification\n    with Mockito\n    with ServiceClientData {\n\n  trait ServiceClientScope extends Scope {\n\n    val httpClient = mock[HttpClient]\n\n    val serviceClient = new ServiceClient(httpClient, baseUrl)\n\n  }\n\n}\n\ncase class Test(value: Int)\n\nclass ServiceClientSpec extends ServiceClientSpecification {\n\n  \"get method from ServiceClient\" should {\n\n    \"return a valid response when service return a valid response\" in\n      new ServiceClientScope {\n\n        httpClient.doGet(any, any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient.get(path, headers, Some(readsResponse), emptyResponse = false) mustRight {\n          r =>\n            r.statusCode shouldEqual statusCodeOk\n            r.data shouldEqual sampleResponse\n        }\n\n        there was one(httpClient).doGet(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return None when the service return a valid empty response\" in\n      new ServiceClientScope {\n\n        httpClient.doGet(any, any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient.get[Unit](path, headers, None, emptyResponse = true) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beNone\n        }\n\n        there was one(httpClient).doGet(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return an exception\" in\n      new ServiceClientScope {\n\n        httpClient.doGet(any, any) returns serviceLeft(exception)\n\n        serviceClient\n          .get(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doGet(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a status code of 'Not Found'\" in\n      new ServiceClientScope {\n\n        httpClient.doGet(any, any) returns serviceRight(notFoundHttpClientResponse)\n\n        serviceClient\n          .get(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doGet(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a invalid JSON in the response\" in\n      new ServiceClientScope {\n\n        httpClient.doGet(any, any) returns serviceRight(invalidHttpClientResponse)\n\n        serviceClient\n          .get(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doGet(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return an empty response but we're expecting some\" in\n      new ServiceClientScope {\n\n        httpClient.doGet(any, any) returns serviceRight(validEmptyHttpClientResponse)\n\n        serviceClient\n          .get(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doGet(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a valid response but the JSON reads is not provided'\" in\n      new ServiceClientScope {\n\n        httpClient.doGet(any, any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient\n          .get(path, headers, None, emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doGet(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n  }\n\n  \"delete method from ServiceClient\" should {\n\n    \"return a valid response when service return a valid response\" in\n      new ServiceClientScope {\n\n        httpClient.doDelete(any, any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient.delete(path, headers, Some(readsResponse), emptyResponse = false) mustRight {\n          r =>\n            r.statusCode shouldEqual statusCodeOk\n            r.data shouldEqual sampleResponse\n        }\n\n        there was one(httpClient).doDelete(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return None when the service return a valid empty response\" in\n      new ServiceClientScope {\n\n        httpClient.doDelete(any, any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient.delete[Unit](path, headers, None, emptyResponse = true) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beNone\n        }\n\n        there was one(httpClient).doDelete(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return an exception\" in\n      new ServiceClientScope {\n\n        httpClient.doDelete(any, any) returns serviceLeft(exception)\n\n        serviceClient\n          .delete(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doDelete(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a status code of 'Not Found'\" in\n      new ServiceClientScope {\n\n        httpClient.doDelete(any, any) returns serviceRight(notFoundHttpClientResponse)\n\n        serviceClient\n          .delete(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doDelete(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a invalid JSON in the response\" in\n      new ServiceClientScope {\n\n        httpClient.doDelete(any, any) returns serviceRight(invalidHttpClientResponse)\n\n        serviceClient\n          .delete(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doDelete(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return an empty response but we're expecting some\" in\n      new ServiceClientScope {\n\n        httpClient.doDelete(any, any) returns serviceRight(validEmptyHttpClientResponse)\n\n        serviceClient\n          .delete(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doDelete(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a valid response but the JSON reads is not provided'\" in\n      new ServiceClientScope {\n\n        httpClient.doDelete(any, any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient\n          .delete(path, headers, None, emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doDelete(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n  }\n\n  \"emptyPost method from ServiceClient\" should {\n\n    \"return a valid response when service return a valid response\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient\n          .emptyPost(path, headers, Some(readsResponse), emptyResponse = false) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data shouldEqual sampleResponse\n        }\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return None when the service return a valid empty response\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient.emptyPost[Unit](path, headers, None, emptyResponse = true) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beNone\n        }\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return an exception\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any) returns serviceLeft(exception)\n\n        serviceClient\n          .emptyPost(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a status code of 'Not Found'\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any) returns serviceRight(notFoundHttpClientResponse)\n\n        serviceClient\n          .emptyPost(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a invalid JSON in the response\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any) returns serviceRight(invalidHttpClientResponse)\n\n        serviceClient\n          .emptyPost(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return an empty response but we're expecting some\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any) returns serviceRight(validEmptyHttpClientResponse)\n\n        serviceClient\n          .emptyPost(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a valid response but the JSON reads is not provided'\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient\n          .emptyPost(path, headers, None, emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n  }\n\n  \"post method from ServiceClient\" should {\n\n    \"return a valid response when service return a valid response\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any, any)(any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient.post(\n          path,\n          headers,\n          sampleRequest,\n          Some(readsResponse),\n          emptyResponse = false) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data shouldEqual sampleResponse\n        }\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return None when the service return a valid empty response\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any, any)(any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient.post[SampleRequest, Unit](\n          path,\n          headers,\n          sampleRequest,\n          None,\n          emptyResponse = true) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beNone\n        }\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return an exception\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any, any)(any) returns serviceLeft(exception)\n\n        serviceClient\n          .post(path, headers, sampleRequest, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a status code of 'Not Found'\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any, any)(any) returns serviceRight(notFoundHttpClientResponse)\n\n        serviceClient\n          .post(path, headers, sampleRequest, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a invalid JSON in the response\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any, any)(any) returns serviceRight(invalidHttpClientResponse)\n\n        serviceClient\n          .post(path, headers, sampleRequest, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return an empty response but we're expecting some\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any, any)(any) returns serviceRight(validEmptyHttpClientResponse)\n\n        serviceClient\n          .post(path, headers, sampleRequest, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a valid response but the JSON reads is not provided'\" in\n      new ServiceClientScope {\n\n        httpClient.doPost(any, any, any)(any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient\n          .post(path, headers, sampleRequest, None, emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPost(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n  }\n\n  \"emptyPut method from ServiceClient\" should {\n\n    \"return a valid response when service return a valid response\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient\n          .emptyPut(path, headers, Some(readsResponse), emptyResponse = false) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data shouldEqual sampleResponse\n        }\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return None when the service return a valid empty response\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient.emptyPut[Unit](path, headers, None, emptyResponse = true) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beNone\n        }\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return an exception\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any) returns serviceLeft(exception)\n\n        serviceClient\n          .emptyPut(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a status code of 'Not Found'\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any) returns serviceRight(notFoundHttpClientResponse)\n\n        serviceClient\n          .emptyPut(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a invalid JSON in the response\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any) returns serviceRight(invalidHttpClientResponse)\n\n        serviceClient\n          .emptyPut(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return an empty response but we're expecting some\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any) returns serviceRight(validEmptyHttpClientResponse)\n\n        serviceClient\n          .emptyPut(path, headers, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a valid response but the JSON reads is not provided'\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient\n          .emptyPut(path, headers, None, emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers)\n        there was noMoreCallsTo(httpClient)\n      }\n  }\n\n  \"put method from ServiceClient\" should {\n\n    \"return a valid response when service return a valid response\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any, any)(any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient\n          .put(path, headers, sampleRequest, Some(readsResponse), emptyResponse = false) mustRight {\n          r =>\n            r.statusCode shouldEqual statusCodeOk\n            r.data shouldEqual sampleResponse\n        }\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return None when the service return a valid empty response\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any, any)(any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient.put[SampleRequest, Unit](\n          path,\n          headers,\n          sampleRequest,\n          None,\n          emptyResponse = true) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beNone\n        }\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return an exception\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any, any)(any) returns serviceLeft(exception)\n\n        serviceClient\n          .put(path, headers, sampleRequest, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a status code of 'Not Found'\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any, any)(any) returns serviceRight(notFoundHttpClientResponse)\n\n        serviceClient\n          .put(path, headers, sampleRequest, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a invalid JSON in the response\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any, any)(any) returns serviceRight(invalidHttpClientResponse)\n\n        serviceClient\n          .put(path, headers, sampleRequest, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return an empty response but we're expecting some\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any, any)(any) returns serviceRight(validEmptyHttpClientResponse)\n\n        serviceClient\n          .put(path, headers, sampleRequest, Some(readsResponse), emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n\n    \"return ServiceClientException when the service return a valid response but the JSON reads is not provided'\" in\n      new ServiceClientScope {\n\n        httpClient.doPut(any, any, any)(any) returns serviceRight(validHttpClientResponse)\n\n        serviceClient\n          .put(path, headers, sampleRequest, None, emptyResponse = false)\n          .mustLeft[ServiceClientException]\n\n        there was one(httpClient).doPut(s\"$baseUrl$path\", headers, sampleRequest)(writesRequest)\n        there was noMoreCallsTo(httpClient)\n      }\n  }\n\n}\n"
  },
  {
    "path": "modules/api/src/test/scala/cards/nine/api/rest/client/http/OkHttpClientSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.rest.client.http\n\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.api.rest.client.SampleRequest\nimport okhttp3.Request\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\nimport play.api.libs.json.Json\n\ntrait OkHttpClientSpecification\n    extends TaskServiceSpecification\n    with DisjunctionMatchers\n    with Mockito {\n\n  trait OkHttpClientScope extends Scope {\n\n    val baseUrl = \"http://sampleUrl\"\n\n    implicit val readsRequest  = Json.reads[SampleRequest]\n    implicit val writesRequest = Json.writes[SampleRequest]\n\n    val acceptedMethod: Option[String] = None\n\n    val acceptedBody: Option[SampleRequest] = None\n\n    val acceptedHeaders = Seq((\"header1\", \"value1\"), (\"header2\", \"value2\"))\n\n    val request = new okhttp3.Request.Builder().url(baseUrl).build()\n\n    val statusCode = 200\n\n    val message = \"Hello World!\"\n\n    val json = s\"\"\"{ \"message\" : \"$message\" }\"\"\"\n\n    val baseException = new IllegalArgumentException(\"\")\n\n    val okHttpResponse = new okhttp3.Response.Builder()\n      .protocol(okhttp3.Protocol.HTTP_1_1)\n      .request(request)\n      .code(statusCode)\n      .message(\"Alright\")\n      .body(okhttp3.ResponseBody.create(okhttp3.MediaType.parse(\"application/json\"), json))\n      .build()\n\n    private def isValidMethod(req: okhttp3.Request): Boolean =\n      acceptedMethod.getOrElse(req.method) == req.method\n\n    private def isValidBody(req: okhttp3.Request): Boolean =\n      acceptedBody match {\n        case Some(r) =>\n          val buffer = new okio.Buffer()\n          req.body().writeTo(buffer)\n          Json.parse(buffer.readUtf8()).as[SampleRequest](readsRequest) == r\n        case _ => true\n      }\n\n    private def isValidHeaders(req: okhttp3.Request): Boolean = {\n      val headers = 0 until req.headers().size() map { index =>\n        val name = req.headers().name(index)\n        (name, req.header(name))\n      }\n      headers == acceptedHeaders\n    }\n\n    val okHttpClient = new OkHttpClient(new okhttp3.OkHttpClient() {\n      override def newCall(theRequest: okhttp3.Request): okhttp3.Call = {\n        new okhttp3.Call {\n\n          override def request(): Request = theRequest\n\n          override def cancel(): Unit = {}\n\n          override def isCanceled: Boolean = false\n\n          override def isExecuted: Boolean = false\n\n          override def enqueue(responseCallback: okhttp3.Callback): Unit = {}\n\n          override def execute(): okhttp3.Response = {\n            if (isValidMethod(theRequest) && isValidHeaders(theRequest) && isValidBody(theRequest))\n              okHttpResponse\n            else\n              throw baseException\n          }\n        }\n      }\n    })\n  }\n\n}\n\nclass OkHttpClientSpec extends OkHttpClientSpecification {\n\n  \"OkHttpClient component\" should {\n\n    \"return the response for a successfully get request\" in new OkHttpClientScope {\n\n      override val acceptedMethod = Some(Methods.GET.toString)\n\n      val response = okHttpClient.doGet(baseUrl, acceptedHeaders).run\n\n      response shouldEqual Right(HttpClientResponse(statusCode, Some(json)))\n    }\n\n    \"return the response for a successfully delete request\" in new OkHttpClientScope {\n\n      override val acceptedMethod = Some(Methods.DELETE.toString)\n\n      val response = okHttpClient.doDelete(baseUrl, acceptedHeaders).run\n\n      response shouldEqual Right(HttpClientResponse(statusCode, Some(json)))\n    }\n\n    \"return the response for a successfully empty post request\" in new OkHttpClientScope {\n\n      override val acceptedMethod = Some(Methods.POST.toString)\n\n      val response = okHttpClient.doPost(baseUrl, acceptedHeaders).run\n\n      response shouldEqual Right(HttpClientResponse(statusCode, Some(json)))\n    }\n\n    \"return the response for a successfully post request\" in new OkHttpClientScope {\n\n      override val acceptedMethod = Some(Methods.POST.toString)\n\n      val sampleRequest         = SampleRequest(\"request\")\n      override val acceptedBody = Some(sampleRequest)\n\n      val response =\n        okHttpClient.doPost[SampleRequest](baseUrl, acceptedHeaders, sampleRequest).run\n\n      response shouldEqual Right(HttpClientResponse(statusCode, Some(json)))\n    }\n\n    \"return the response for a successfully empty put request\" in new OkHttpClientScope {\n\n      override val acceptedMethod = Some(Methods.PUT.toString)\n\n      val response = okHttpClient.doPut(baseUrl, acceptedHeaders).run\n\n      response shouldEqual Right(HttpClientResponse(statusCode, Some(json)))\n    }\n\n    \"return the response for a successfully put request\" in new OkHttpClientScope {\n\n      override val acceptedMethod = Some(Methods.PUT.toString)\n\n      val sampleRequest         = SampleRequest(\"request\")\n      override val acceptedBody = Some(sampleRequest)\n\n      val response = okHttpClient.doPut[SampleRequest](baseUrl, acceptedHeaders, sampleRequest).run\n\n      response shouldEqual Right(HttpClientResponse(statusCode, Some(json)))\n    }\n\n    \"return an Exception for an unexpected method\" in new OkHttpClientScope {\n\n      override val acceptedMethod = Some(Methods.GET.toString)\n\n      val response = okHttpClient.doDelete(baseUrl, Seq.empty).run\n\n      response must beAnInstanceOf[Left[IllegalArgumentException, _]]\n    }\n\n    \"return an Exception for an unexpected request\" in new OkHttpClientScope {\n\n      override val acceptedMethod = Some(Methods.POST.toString)\n\n      override val acceptedBody = Some(SampleRequest(\"request\"))\n\n      val response =\n        okHttpClient.doPut[SampleRequest](baseUrl, Seq.empty, SampleRequest(\"bad_request\")).run\n\n      response must beAnInstanceOf[Left[IllegalArgumentException, _]]\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/api/src/test/scala/cards/nine/api/version1/ApiServiceData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.version1\n\nimport play.api.libs.json.JsNull\n\ntrait ApiServiceData {\n\n  val baseUrl = \"http://localhost:8080\"\n\n  val statusCodeOk = 200\n\n  val userId = 10\n\n  val sessionToken = \"session-token\"\n\n  val username = \"username\"\n  val password = \"password\"\n  val email    = \"email\"\n\n  val deviceId    = \"device-id\"\n  val deviceName  = \"Nexus 47\"\n  val secretToken = \"secret-token\"\n  val permission  = \"androidmarket\"\n\n  val headers = Seq(\n    (\"header-1\", \"header-1-value\"),\n    (\"header-2\", \"header-2-value\"),\n    (\"header-3\", \"header-3-value\"))\n\n  val authGoogleDevice = AuthGoogleDevice(\n    name = deviceName,\n    deviceId = deviceId,\n    secretToken = secretToken,\n    permissions = Seq(permission))\n\n  val authGoogle = AuthGoogle(email = email, devices = Seq(authGoogleDevice))\n\n  val authData =\n    AuthData(google = Some(authGoogle), facebook = None, twitter = None, anonymous = None)\n\n  val emptyUser = User(None, None, None, None, None, None)\n\n  val user = User(\n    Some(userId.toString),\n    Some(sessionToken),\n    Some(username),\n    Some(password),\n    Some(email),\n    authData = Some(authData))\n\n  val userConfigProfileImage =\n    UserConfigProfileImage(imageType = 0, imageUrl = \"http://fakeUrl\", secureUrl = None)\n\n  val userConfigPlusProfile = UserConfigPlusProfile(username, userConfigProfileImage)\n\n  val userConfigCollectionItem = UserConfigCollectionItem(\n    itemType = \"item type\",\n    title = \"item title\",\n    metadata = JsNull,\n    categories = None)\n\n  val userConfigCollection = UserConfigCollection(\n    name = \"collection name\",\n    originalSharedCollectionId = Some(\"original collection id\"),\n    sharedCollectionId = Some(\"collection id\"),\n    sharedCollectionSubscribed = Some(true),\n    items = Seq(userConfigCollectionItem),\n    collectionType = \"collection type\",\n    constrains = Seq.empty,\n    wifi = Seq.empty,\n    occurrence = Seq.empty,\n    icon = \"social\",\n    radius = 0,\n    lat = 0,\n    lng = 0,\n    alt = 0,\n    category = Some(\"SOCIAL\"))\n\n  val userConfigDevice = UserConfigDevice(deviceId, deviceName, Seq(userConfigCollection))\n\n  val userConfigGeoInfo = UserConfigGeoInfo(None, None, None, None)\n\n  val userConfigStatusInfo = UserConfigStatusInfo(\n    products = Seq.empty,\n    friendsReferred = 10,\n    themesShared = 5,\n    collectionsShared = 5,\n    customCollections = 2,\n    earlyAdopter = false,\n    communityMember = true,\n    joinedThrough = None,\n    tester = false)\n\n  val userConfig = UserConfig(\n    userId.toString,\n    email,\n    userConfigPlusProfile,\n    Seq(userConfigDevice),\n    userConfigGeoInfo,\n    userConfigStatusInfo)\n\n}\n"
  },
  {
    "path": "modules/api/src/test/scala/cards/nine/api/version1/ApiServiceSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.version1\n\nimport cats.syntax.either._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.api.rest.client.ServiceClient\nimport cards.nine.api.rest.client.messages.ServiceClientResponse\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait ApiServiceSpecification extends TaskServiceSpecification with Mockito with ApiServiceData {\n\n  trait ApiServiceScope extends Scope {\n\n    val mockedServiceClient = mock[ServiceClient]\n\n    mockedServiceClient.baseUrl returns baseUrl\n\n    val apiService = new ApiService(mockedServiceClient)\n\n  }\n\n}\n\nclass ApiServiceSpec extends ApiServiceSpecification {\n\n  import JsonImplicits._\n\n  \"login\" should {\n\n    \"return the status code and the response\" in new ApiServiceScope {\n\n      mockedServiceClient.post[User, User](any, any, any, any, any)(any) returns\n        TaskService(Task(Either.right(ServiceClientResponse(statusCodeOk, Some(user)))))\n\n      apiService.login(emptyUser, headers) mustRight { r =>\n        r.statusCode shouldEqual statusCodeOk\n        r.data must beSome(user)\n      }\n\n      there was one(mockedServiceClient).post(\n        path = \"/users\",\n        headers = headers,\n        body = emptyUser,\n        reads = Some(userReads),\n        emptyResponse = false)(userWrites)\n\n    }\n\n  }\n\n  \"getUserConfig\" should {\n\n    \"return the status code and the response\" in new ApiServiceScope {\n\n      mockedServiceClient.get[UserConfig](any, any, any, any) returns\n        TaskService(Task(Either.right(ServiceClientResponse(statusCodeOk, Some(userConfig)))))\n\n      apiService.getUserConfig(headers) mustRight { r =>\n        r.statusCode shouldEqual statusCodeOk\n        r.data must beSome(userConfig)\n      }\n\n      there was one(mockedServiceClient).get(\n        path = \"/ninecards/userconfig\",\n        headers = headers,\n        reads = Some(userConfigReads),\n        emptyResponse = false)\n\n    }\n\n  }\n}\n"
  },
  {
    "path": "modules/api/src/test/scala/cards/nine/api/version2/ApiServiceData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.version2\n\nimport scala.util.Random\n\ntrait ApiServiceData {\n\n  val baseUrl = \"http://localhost:8080\"\n\n  val headerContentType             = \"Content-Type\"\n  val headerContentTypeValue        = \"application/json\"\n  val headerAuthToken               = \"X-Auth-Token\"\n  val headerSessionToken            = \"X-Session-Token\"\n  val headerAndroidId               = \"X-Android-ID\"\n  val headerMarketLocalization      = \"X-Android-Market-Localization\"\n  val headerMarketLocalizationValue = \"en-US\"\n  val headerAndroidMarketToken      = \"X-Google-Play-Token\"\n\n  val statusCodeOk = 200\n\n  val email        = \"email@dot.com\"\n  val loginId      = \"login-id\"\n  val tokenId      = \"token-id\"\n  val apiKey       = \"api-key\"\n  val deviceToken  = \"device-token\"\n  val sessionToken = \"session-token\"\n  val androidId    = \"android-id\"\n  val marketToken  = \"market-token\"\n\n  val installationAuthToken =\n    \"f9dfeb34c16dae2715dc85b82f7d2a91ff3999212ed5235950d3328c0ab8e35886c98a788ed5fa35211be0a2b7829306b83c7cc4c954025d2b69786608666785\"\n  val latestCollectionsAuthToken =\n    \"eeec8eee60eba7e312cfc752396c0f05625bc372c95e1c6e70ea0e02b61f23e1f238b42c133fedcba2eb725c3e6a14542c2070d8db40e7ffa5a74eb8f50b8f60\"\n  val topCollectionsAuthToken =\n    \"7d7290b7c03da9cf5000b05c148ed430bb333c67a47d57085438481e05f683428b9ad662309fdb637f925bd1eb85b8eefcd522b206314977028f29724ca922d2\"\n  val collectionsAuthToken =\n    \"c5284c3a5ff576a984689bd13ff3e6144dc5ebfedcd14f4eb7ba5b8b8f5ff1211b677fdf12ff8fe93d226de1fb45c706578c4ee43d47dda5cde51c91a7c5bb06\"\n  val collectionsIdAuthToken =\n    \"5fdf0285acf5b1f0223c903553558a7512c92aabac6e6c2dd1daa794682966f954438645ce6cd139036ace029e38c609a23aaeabcdd453d25a5349d8b5cf178c\"\n  val categorizeAuthToken =\n    \"4f129e296588493aab55e0192894ed95867546674844479f8dce0f0f506eed80991a1f09d459d476335acdf27e3f178d16f8df94c45a0e77d4f10935f8199493\"\n  val categorizeDetailAuthToken =\n    \"f6ccbb142c90bb3b527ee2d27867daf4324403255fe90f4f5e0b4b25f992fa7b0c4635a7161fc104f664ea7bea337afe1fffc3e12c4c597eeee56557d089d23b\"\n  val recommendationsAuthToken =\n    \"915f6db594dd9be2bc9cfb2a232c52bccee633afe97086cca7f468690ad8ca7bb87747b5ee13a35af0103091bf9b0d5a38f0cccd5179ca98b399c65f7afae6a2\"\n  val recommendationsByAppsAuthToken =\n    \"ee388db874ece41e4a446bb8c36f0967944d71d87239ae5d629ee6db074508e318eb70c134572d9a3d10192e07b11135360a539a302ca347f3bbdeec6970129b\"\n  val subscriptionsAuthToken =\n    \"b45ad90c3d18f43b7b921c2aeec3258a8a1adf11ad75e4c68928ac89855c5c019b921bc268bc3072a2735718479809a71810e684b11051619ed564392d5be3dd\"\n  val rankAppsAuthToken =\n    \"b105db467bb6d9087471aeeba4337bb8c7b54a383ddbe711dd6b36536c053ec0520ab3fb99dbe03471dd5a6a09001b80dd784ee16684cb71ac6c063926e5d109\"\n  val searchAuthToken =\n    \"1f6f5235a235a4edd3bf7108d1d898a1df219219290c1409d231e6213f47180d9be85e299019434ee9339da83ae28eb7a83dcd95464bf0edbb20cbc1b86506c4\"\n  val updateCollectionAuthToken =\n    \"5fc154fa279c6cb9350d502289e737e3a568ab7ceec6f3b97628644406c100337c88387b47b5ed894582cc01b35bfba156a0d224a5edc392ef23e15811b53d79\"\n\n  val serviceHeader = ServiceHeader(apiKey, sessionToken, androidId)\n\n  val serviceMarketHeader = ServiceMarketHeader(apiKey, sessionToken, androidId, Some(marketToken))\n\n  def createHeaders(authToken: String) =\n    Seq(\n      (headerContentType, headerContentTypeValue),\n      (headerAuthToken, authToken),\n      (headerSessionToken, sessionToken),\n      (headerAndroidId, androidId),\n      (headerMarketLocalization, headerMarketLocalizationValue))\n\n  def createMarketHeaders(authToken: String) =\n    createHeaders(authToken) :+ (headerAndroidMarketToken, marketToken)\n\n  val category         = \"SOCIAL\"\n  val publicIdentifier = \"collection-public-identifier\"\n  val offset           = 0\n  val limit            = 100\n\n  val collectionName   = \"collection name\"\n  val collectionAuthor = \"collection author\"\n  val collectionIcon   = \"collection icon\"\n\n  val collectionApp = CollectionApp(\n    stars = 3.5,\n    icon = \"app icon\",\n    packageName = \"com.package.app\",\n    downloads = \"500,000,000+\",\n    categories = Seq(category),\n    title = \"app title\",\n    free = true)\n\n  val collection = Collection(\n    name = collectionName,\n    author = collectionAuthor,\n    owned = false,\n    icon = collectionIcon,\n    category = category,\n    community = true,\n    publishedOn = \"2016-08-16T14:55:30.574000\",\n    installations = Some(10),\n    views = Some(100),\n    subscriptions = Some(100),\n    publicIdentifier = publicIdentifier,\n    appsInfo = Seq(collectionApp),\n    packages = Seq(collectionApp.packageName))\n\n  val createCollectionRequest = CreateCollectionRequest(\n    name = collectionName,\n    author = collectionAuthor,\n    icon = collectionIcon,\n    category = category,\n    community = true,\n    packages = Seq(collectionApp.packageName))\n\n  val createCollectionResponse =\n    CreateCollectionResponse(publicIdentifier = publicIdentifier, packagesStats = PackagesStats(1))\n\n  val updateCollectionRequest = UpdateCollectionRequest(\n    collectionInfo = Some(CollectionUpdateInfo(title = collectionName)),\n    packages = Some(Seq(collectionApp.packageName)))\n\n  val updateCollectionResponse =\n    UpdateCollectionResponse(publicIdentifier = publicIdentifier, packagesStats = PackagesStats(1))\n\n  val categorizeRequest = CategorizeRequest(items = Seq(collectionApp.packageName))\n\n  val categorizeResponse = CategorizeResponse(\n    errors = Seq.empty,\n    items =\n      Seq(CategorizedApp(packageName = collectionApp.packageName, categories = Seq(category))))\n\n  val categorizeDetailResponse = CategorizeDetailResponse(\n    errors = Seq.empty,\n    items = Seq(\n      CategorizedAppDetail(\n        packageName = collectionApp.packageName,\n        title = collectionApp.title,\n        categories = Seq(category),\n        icon = collectionApp.icon,\n        free = collectionApp.free,\n        downloads = collectionApp.downloads,\n        stars = collectionApp.stars)))\n\n  val notCategorizedApp = NotCategorizedApp(\n    packageName = collectionApp.packageName,\n    title = collectionApp.title,\n    downloads = collectionApp.downloads,\n    icon = collectionApp.icon,\n    stars = collectionApp.stars,\n    free = collectionApp.free,\n    screenshots = Seq(\"screenshot1\", \"screenshot2\", \"screenshot3\"))\n\n  val recommendationsRequest =\n    RecommendationsRequest(excludePackages = Seq(\"com.package.sample\"), limit = 10)\n\n  val recommendationsByAppsRequest = RecommendationsByAppsRequest(\n    packages = Seq(\"com.fortysevendeg.ninecardslauncher\"),\n    excludePackages = Seq(\"com.package.sample\"),\n    limit = 10)\n\n  val recommendationsResponse = RecommendationsResponse(items = Seq(notCategorizedApp))\n\n  val recommendationsByAppsResponse = RecommendationsByAppsResponse(apps = Seq(notCategorizedApp))\n\n  val subscriptions = Seq(publicIdentifier)\n\n  val packages =\n    Seq(\"com.package.sample.second\", \"com.package.sample.third\", \"com.package.sample.first\")\n\n  val packagesOrdered =\n    Seq(\"com.package.sample.first\", \"com.package.sample.second\", \"com.package.sample.third\")\n\n  val items = Map(category -> packages)\n\n  val location = Random.nextBoolean match {\n    case false => None\n    case true  => Some(\"ES\")\n  }\n\n  val rankAppsRequest = RankAppsRequest(items = items, location = location)\n\n  val rankAppsResponse = RankAppsResponse(\n    Seq(RankAppsCategoryResponse(category = category, packages = packagesOrdered)))\n\n  val searchRequest =\n    SearchRequest(query = \"sample query\", excludePackages = Seq(\"com.package.sample\"), limit = 10)\n\n  val searchResponse = SearchResponse(items = Seq(notCategorizedApp))\n\n}\n"
  },
  {
    "path": "modules/api/src/test/scala/cards/nine/api/version2/ApiServiceSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.api.version2\n\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.api.rest.client.ServiceClient\nimport cards.nine.api.rest.client.messages.ServiceClientResponse\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait ApiServiceSpecification extends TaskServiceSpecification with Mockito with ApiServiceData {\n\n  trait ApiServiceScope extends Scope {\n\n    val mockedServiceClient = mock[ServiceClient]\n\n    mockedServiceClient.baseUrl returns baseUrl\n\n    val apiService = new ApiService(mockedServiceClient)\n\n  }\n\n}\n\nclass ApiServiceSpec extends ApiServiceSpecification {\n\n  import JsonImplicits._\n\n  \"login\" should {\n\n    \"return the status code and the response\" in new ApiServiceScope {\n\n      val response = ApiLoginResponse(apiKey, sessionToken)\n\n      mockedServiceClient.post[ApiLoginRequest, ApiLoginResponse](any, any, any, any, any)(any) returns\n        serviceRight(ServiceClientResponse(statusCodeOk, Some(response)))\n\n      val request = ApiLoginRequest(email, loginId, tokenId)\n\n      apiService.login(request) mustRight { r =>\n        r.statusCode shouldEqual statusCodeOk\n        r.data must beSome(response)\n      }\n\n      there was one(mockedServiceClient).post(\n        path = \"/login\",\n        headers = Seq((headerContentType, headerContentTypeValue)),\n        body = request,\n        reads = Some(loginResponseReads),\n        emptyResponse = false)(loginRequestWrites)\n\n    }\n\n  }\n\n  \"installations\" should {\n\n    \"return the status code and the response\" in new ApiServiceScope {\n\n      val response = InstallationResponse(androidId, deviceToken)\n\n      mockedServiceClient.put[InstallationRequest, InstallationResponse](any, any, any, any, any)(\n        any) returns\n        serviceRight(ServiceClientResponse(statusCodeOk, Some(response)))\n\n      val request = InstallationRequest(deviceToken)\n\n      apiService.installations(request, serviceHeader) mustRight { r =>\n        r.statusCode shouldEqual statusCodeOk\n        r.data must beSome(response)\n      }\n\n      there was one(mockedServiceClient).put(\n        path = \"/installations\",\n        headers = createHeaders(installationAuthToken),\n        body = request,\n        reads = Some(installationResponseReads),\n        emptyResponse = false)(installationRequestWrites)\n\n    }\n\n    \"latest collections\" should {\n\n      \"return the status code and the response\" in new ApiServiceScope {\n\n        val response = CollectionsResponse(Seq(collection))\n\n        mockedServiceClient.get[CollectionsResponse](any, any, any, any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(response)))\n\n        apiService.latestCollections(category, offset, limit, serviceMarketHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beSome(response)\n        }\n\n        there was one(mockedServiceClient).get(\n          path = s\"/collections/latest/$category/$offset/$limit\",\n          headers = createMarketHeaders(latestCollectionsAuthToken),\n          reads = Some(collectionsResponseReads),\n          emptyResponse = false)\n\n      }\n\n    }\n\n    \"top collections\" should {\n\n      \"return the status code and the response\" in new ApiServiceScope {\n\n        val response = CollectionsResponse(Seq(collection))\n\n        mockedServiceClient.get[CollectionsResponse](any, any, any, any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(response)))\n\n        apiService.topCollections(category, offset, limit, serviceMarketHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beSome(response)\n        }\n\n        there was one(mockedServiceClient).get(\n          path = s\"/collections/top/$category/$offset/$limit\",\n          headers = createMarketHeaders(topCollectionsAuthToken),\n          reads = Some(collectionsResponseReads),\n          emptyResponse = false)\n\n      }\n\n    }\n\n    \"create collection\" should {\n\n      \"return the status code and the response\" in new ApiServiceScope {\n\n        mockedServiceClient.post[CreateCollectionRequest, CreateCollectionResponse](\n          any,\n          any,\n          any,\n          any,\n          any)(any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(createCollectionResponse)))\n\n        apiService.createCollection(createCollectionRequest, serviceHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beSome(createCollectionResponse)\n        }\n\n        there was one(mockedServiceClient).post(\n          path = \"/collections\",\n          headers = createHeaders(collectionsAuthToken),\n          body = createCollectionRequest,\n          reads = Some(createCollectionResponseReads),\n          emptyResponse = false)(createCollectionRequestWrites)\n\n      }\n\n    }\n\n    \"update collection\" should {\n\n      \"return the status code and the response\" in new ApiServiceScope {\n\n        mockedServiceClient.put[UpdateCollectionRequest, UpdateCollectionResponse](\n          any,\n          any,\n          any,\n          any,\n          any)(any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(updateCollectionResponse)))\n\n        apiService\n          .updateCollection(publicIdentifier, updateCollectionRequest, serviceHeader) mustRight {\n          r =>\n            r.statusCode shouldEqual statusCodeOk\n            r.data must beSome(updateCollectionResponse)\n        }\n\n        there was one(mockedServiceClient).put(\n          path = s\"/collections/$publicIdentifier\",\n          headers = createHeaders(collectionsIdAuthToken),\n          body = updateCollectionRequest,\n          reads = Some(updateCollectionResponseReads),\n          emptyResponse = false)(updateCollectionRequestWrites)\n\n      }\n\n    }\n\n    \"get collection\" should {\n\n      \"return the status code and the response\" in new ApiServiceScope {\n\n        val response = collection\n\n        mockedServiceClient.get[Collection](any, any, any, any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(response)))\n\n        apiService.getCollection(publicIdentifier, serviceMarketHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beSome(response)\n        }\n\n        there was one(mockedServiceClient).get(\n          path = s\"/collections/$publicIdentifier\",\n          headers = createMarketHeaders(collectionsIdAuthToken),\n          reads = Some(collectionReads),\n          emptyResponse = false)\n\n      }\n\n    }\n\n    \"get collections\" should {\n\n      \"return the status code and the response\" in new ApiServiceScope {\n\n        val response = CollectionsResponse(Seq(collection))\n\n        mockedServiceClient.get[CollectionsResponse](any, any, any, any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(response)))\n\n        apiService.getCollections(serviceMarketHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beSome(response)\n        }\n\n        there was one(mockedServiceClient).get(\n          path = \"/collections\",\n          headers = createMarketHeaders(collectionsAuthToken),\n          reads = Some(collectionsResponseReads),\n          emptyResponse = false)\n\n      }\n\n    }\n\n    \"categorize\" should {\n\n      \"return the status code and the response\" in new ApiServiceScope {\n\n        mockedServiceClient.post[CategorizeRequest, CategorizeResponse](any, any, any, any, any)(\n          any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(categorizeResponse)))\n\n        apiService.categorize(categorizeRequest, serviceMarketHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beSome(categorizeResponse)\n        }\n\n        there was one(mockedServiceClient).post(\n          path = \"/applications/categorize\",\n          headers = createMarketHeaders(categorizeAuthToken),\n          body = categorizeRequest,\n          reads = Some(categorizeResponseReads),\n          emptyResponse = false)(categorizeRequestWrites)\n\n      }\n\n    }\n\n    \"categorizeDetail\" should {\n\n      \"return the status code and the response\" in new ApiServiceScope {\n\n        mockedServiceClient\n          .post[CategorizeRequest, CategorizeDetailResponse](any, any, any, any, any)(any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(categorizeDetailResponse)))\n\n        apiService.categorizeDetail(categorizeRequest, serviceMarketHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beSome(categorizeDetailResponse)\n        }\n\n        there was one(mockedServiceClient).post(\n          path = \"/applications/details\",\n          headers = createMarketHeaders(categorizeDetailAuthToken),\n          body = categorizeRequest,\n          reads = Some(categorizeDetailResponseReads),\n          emptyResponse = false)(categorizeRequestWrites)\n\n      }\n\n    }\n\n    \"recommendations\" should {\n\n      \"return the status code and the response and call with the right category\" in new ApiServiceScope {\n\n        val typePay = \"FREE\"\n\n        mockedServiceClient.post[RecommendationsRequest, RecommendationsResponse](\n          any,\n          any,\n          any,\n          any,\n          any)(any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(recommendationsResponse)))\n\n        apiService.recommendations(\n          category,\n          Option(typePay),\n          recommendationsRequest,\n          serviceMarketHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beSome(recommendationsResponse)\n        }\n\n        there was one(mockedServiceClient).post(\n          path = s\"/recommendations/$category/$typePay\",\n          headers = createMarketHeaders(recommendationsAuthToken),\n          body = recommendationsRequest,\n          reads = Some(recommendationsResponseReads),\n          emptyResponse = false)(recommendationsRequestWrites)\n\n      }\n\n    }\n\n    \"recommendations by apps\" should {\n\n      \"return the status code and the response\" in new ApiServiceScope {\n\n        mockedServiceClient.post[RecommendationsByAppsRequest, RecommendationsByAppsResponse](\n          any,\n          any,\n          any,\n          any,\n          any)(any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(recommendationsByAppsResponse)))\n\n        apiService\n          .recommendationsByApps(recommendationsByAppsRequest, serviceMarketHeader) mustRight {\n          r =>\n            r.statusCode shouldEqual statusCodeOk\n            r.data must beSome(recommendationsByAppsResponse)\n        }\n\n        there was one(mockedServiceClient).post(\n          path = s\"/recommendations\",\n          headers = createMarketHeaders(recommendationsByAppsAuthToken),\n          body = recommendationsByAppsRequest,\n          reads = Some(recommendationsByAppsResponseReads),\n          emptyResponse = false)(recommendationsByAppsRequestWrites)\n\n      }\n\n    }\n\n    \"get subscriptions\" should {\n\n      \"return the status code and the response\" in new ApiServiceScope {\n\n        val response = SubscriptionsResponse(subscriptions)\n\n        mockedServiceClient.get[SubscriptionsResponse](any, any, any, any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(response)))\n\n        apiService.getSubscriptions(serviceHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beSome(response)\n        }\n\n        there was one(mockedServiceClient).get(\n          path = \"/collections/subscriptions\",\n          headers = createHeaders(subscriptionsAuthToken),\n          reads = Some(subscriptionsResponseReads))\n\n      }\n\n    }\n\n    \"subscribe\" should {\n\n      \"return the status code\" in new ApiServiceScope {\n\n        mockedServiceClient.emptyPut[Unit](any, any, any, any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, None))\n\n        apiService.subscribe(publicIdentifier, serviceHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beNone\n        }\n\n        there was one(mockedServiceClient).emptyPut(\n          path = s\"/collections/subscriptions/$publicIdentifier\",\n          headers = createHeaders(subscriptionsAuthToken),\n          reads = None,\n          emptyResponse = true)\n\n      }\n\n    }\n\n    \"unsubscribe\" should {\n\n      \"return the status code\" in new ApiServiceScope {\n\n        mockedServiceClient.delete[Unit](any, any, any, any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, None))\n\n        apiService.unsubscribe(publicIdentifier, serviceHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beNone\n        }\n\n        there was one(mockedServiceClient).delete(\n          path = s\"/collections/subscriptions/$publicIdentifier\",\n          headers = createHeaders(subscriptionsAuthToken),\n          reads = None,\n          emptyResponse = true)\n\n      }\n\n    }\n\n    \"updateViewShareCollection\" should {\n\n      \"return the status code\" in new ApiServiceScope {\n\n        mockedServiceClient.emptyPost[Unit](any, any, any, any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, None))\n\n        apiService.updateViewShareCollection(publicIdentifier, serviceHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beNone\n        }\n\n        there was one(mockedServiceClient).emptyPost(\n          path = s\"/collections/$publicIdentifier/views\",\n          headers = createHeaders(updateCollectionAuthToken),\n          reads = None,\n          emptyResponse = true)\n\n      }\n\n    }\n\n    \"rank apps\" should {\n\n      \"return the status code and the response\" in new ApiServiceScope {\n\n        mockedServiceClient.post[RankAppsRequest, RankAppsResponse](any, any, any, any, any)(any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(rankAppsResponse)))\n\n        apiService.rankApps(rankAppsRequest, serviceHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beSome(rankAppsResponse)\n        }\n\n        there was one(mockedServiceClient).post(\n          path = \"/applications/rank\",\n          headers = createHeaders(rankAppsAuthToken),\n          body = rankAppsRequest,\n          reads = Some(rankAppsResponseReads),\n          emptyResponse = false)(rankAppsRequestWrites)\n\n      }\n\n    }\n\n    \"search apps\" should {\n\n      \"return the status code and the response\" in new ApiServiceScope {\n\n        mockedServiceClient.post[SearchRequest, SearchResponse](any, any, any, any, any)(any) returns\n          serviceRight(ServiceClientResponse(statusCodeOk, Some(searchResponse)))\n\n        apiService.search(searchRequest, serviceMarketHeader) mustRight { r =>\n          r.statusCode shouldEqual statusCodeOk\n          r.data must beSome(searchResponse)\n        }\n\n        there was one(mockedServiceClient).post(\n          path = \"/applications/search\",\n          headers = createMarketHeaders(searchAuthToken),\n          body = searchRequest,\n          reads = Some(searchResponseReads),\n          emptyResponse = false)(searchRequestWrites)\n\n      }\n\n    }\n\n  }\n}\n"
  },
  {
    "path": "modules/api/src/test/scala/com/fortysevendeg/BaseTestSupport.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.fortysevendeg\n\nimport org.specs2.execute.{AsResult, Result}\nimport org.specs2.specification.{Around, Scope}\n\ntrait BaseTestSupport extends Around with Scope {\n\n  override def around[T: AsResult](t: => T): Result = AsResult.effectively(t)\n\n}\n"
  },
  {
    "path": "modules/app/build.sbt",
    "content": "platformTarget in Android := \"android-24\"\n"
  },
  {
    "path": "modules/app/crashlytics/templates/CrashlyticsManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"com.fortysevendeg.ninecardslauncher\"\n          android:versionCode=\"1\"\n          android:versionName=\"1.0\">\n\n    <application\n        android:label=\"@string/app_name\"\n        android:icon=\"@drawable/ic_launcher\">\n\n        <meta-data\n            android:name=\"io.fabric.ApiKey\"\n            android:value=\"${crashlytics.apikey}\"/>\n\n    </application>\n\n</manifest>"
  },
  {
    "path": "modules/app/project/plugins.sbt",
    "content": "addSbtPlugin(\"com.hanhuy.sbt\" % \"android-sdk-plugin\" % \"1.5.1\")\n"
  },
  {
    "path": "modules/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"com.fortysevendeg.ninecardslauncher\">\n\n    <uses-sdk\n        android:minSdkVersion=\"16\"\n        android:targetSdkVersion=\"24\"/>\n\n    <application\n        android:label=\"@string/app_name\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:name=\"cards.nine.app.NineCardsApplication\">\n\n        <meta-data\n            android:name=\"com.google.android.awareness.API_KEY\"\n            android:value=\"@string/google_api_key\"/>\n\n        <meta-data\n            android:name=\"com.google.android.geo.API_KEY\"\n            android:value=\"@string/google_api_key\" />\n\n        <meta-data android:name=\"io.fabric.ApiKey\"\n                   android:value=\"@string/crashlytics_api_key\"/>\n\n        <activity\n            android:theme=\"@style/AppThemeWallpaper\"\n            android:launchMode=\"singleTask\"\n            android:clearTaskOnLaunch=\"true\"\n            android:stateNotNeeded=\"true\"\n            android:windowSoftInputMode=\"adjustPan\"\n            android:screenOrientation=\"portrait\"\n            android:name=\"cards.nine.app.ui.launcher.LauncherActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.HOME\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.MONKEY\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:theme=\"@style/AppTheme\"\n            android:launchMode=\"singleTask\"\n            android:clearTaskOnLaunch=\"true\"\n            android:screenOrientation=\"portrait\"\n            android:label=\"\"\n            android:name=\"cards.nine.app.ui.collections.CollectionsDetailsActivity\"/>\n\n        <activity\n            android:theme=\"@style/AppTheme\"\n            android:launchMode=\"singleTask\"\n            android:screenOrientation=\"portrait\"\n            android:label=\"\"\n            android:name=\"cards.nine.app.ui.wizard.WizardActivity\" />\n\n        <activity\n            android:theme=\"@style/AppTheme\"\n            android:screenOrientation=\"portrait\"\n            android:label=\"\"\n            android:name=\"cards.nine.app.ui.profile.ProfileActivity\"/>\n\n        <activity\n            android:theme=\"@style/AppThemePreferences\"\n            android:screenOrientation=\"portrait\"\n            android:label=\"@string/nineCardsSettingsTitle\"\n            android:name=\"cards.nine.app.ui.preferences.NineCardsPreferencesActivity\"/>\n\n        <activity\n            android:theme=\"@style/AppThemeSharedContent\"\n            android:launchMode=\"singleTask\"\n            android:screenOrientation=\"portrait\"\n            android:label=\"@string/sharedIntentLabel\"\n            android:name=\"cards.nine.app.ui.share.SharedContentActivity\" >\n            <intent-filter\n                android:label=\"@string/sharedIntentLabel\">\n                <action android:name=\"android.intent.action.SEND\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <data android:mimeType=\"text/plain\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:theme=\"@style/AppCompatDialog\"\n            android:launchMode=\"singleTask\"\n            android:name=\"cards.nine.app.ui.applinks.AppLinksReceiverActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n                <data android:scheme=\"http\" />\n                <data android:scheme=\"https\" />\n                <data android:host=\"9c.io\" android:pathPrefix=\"/shared-collection\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n                <data android:scheme=\"http\" />\n                <data android:scheme=\"https\" />\n                <data android:host=\"www.9c.io\" android:pathPrefix=\"/shared-collection\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n                <data android:scheme=\"http\" />\n                <data android:scheme=\"https\" />\n                <data android:host=\"www.9cards.io\" android:pathPrefix=\"/shared-collection\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n                <data android:scheme=\"http\" />\n                <data android:scheme=\"https\" />\n                <data android:host=\"nine.cards\" android:pathPrefix=\"/shared-collection\" />\n            </intent-filter>\n        </activity>\n\n        <service\n            android:name=\"cards.nine.app.services.sync.SynchronizeDeviceService\"/>\n\n        <provider\n            android:authorities=\"com.fortysevendeg.ninecardslauncher\"\n            android:name=\"cards.nine.repository.provider.NineCardsContentProvider\"\n            android:exported=\"false\"/>\n\n        <receiver android:name=\"cards.nine.app.receivers.apps.AppBroadcastReceiver\">\n            <intent-filter android:priority=\"999\">\n                <action android:name=\"android.intent.action.PACKAGE_ADDED\"/>\n                <action android:name=\"android.intent.action.PACKAGE_REMOVED\"/>\n                <action android:name=\"android.intent.action.PACKAGE_CHANGED\"/>\n                <action android:name=\"android.intent.action.PACKAGE_REPLACED\"/>\n\n                <data android:scheme=\"package\"/>\n            </intent-filter>\n        </receiver>\n\n        <receiver android:name=\"cards.nine.app.receivers.moments.MomentBroadcastReceiver\">\n            <intent-filter>\n                <action android:name=\"android.net.conn.CONNECTIVITY_CHANGE\"/>\n            </intent-filter>\n        </receiver>\n\n        <!-- Gooogle Analytics Receivers and Services -->\n        <receiver android:name=\"com.google.android.gms.analytics.AnalyticsReceiver\"\n                  android:enabled=\"true\">\n            <intent-filter>\n                <action android:name=\"com.google.android.gms.analytics.ANALYTICS_DISPATCH\" />\n            </intent-filter>\n        </receiver>\n\n        <service android:name=\"com.google.android.gms.analytics.AnalyticsService\"\n                 android:enabled=\"true\"\n                 android:exported=\"false\"/>\n\n        <receiver android:name=\"com.google.android.gms.analytics.CampaignTrackingReceiver\"\n                  android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"com.android.vending.INSTALL_REFERRER\" />\n            </intent-filter>\n        </receiver>\n\n        <receiver\n            android:name=\"cards.nine.app.receivers.shortcuts.ShortcutBroadcastReceiver\"\n            android:permission=\"com.android.launcher.permission.INSTALL_SHORTCUT\">\n            <intent-filter>\n                <action android:name=\"com.android.launcher.action.INSTALL_SHORTCUT\"/>\n            </intent-filter>\n        </receiver>\n\n        <receiver\n            android:name=\"cards.nine.app.receivers.bluetooth.BluetoothReceiver\"\n            android:enabled=\"true\">\n            <intent-filter>\n                <action android:name=\"android.bluetooth.device.action.ACL_CONNECTED\" />\n                <action android:name=\"android.bluetooth.device.action.ACL_DISCONNECTED\" />\n                <action android:name=\"android.bluetooth.adapter.action.STATE_CHANGED\" />\n            </intent-filter>\n        </receiver>\n\n        <service\n            android:name=\"com.google.android.gms.analytics.CampaignTrackingService\" />\n\n        <service\n            android:name=\"cards.nine.app.services.NineCardsFirebaseInstanceIdService\">\n            <intent-filter>\n                <action android:name=\"com.google.firebase.INSTANCE_ID_EVENT\"/>\n            </intent-filter>\n        </service>\n\n        <service\n            android:name=\"cards.nine.app.services.NineCardsFirebaseMessagingService\">\n            <intent-filter>\n                <action android:name=\"com.google.firebase.MESSAGING_EVENT\"/>\n            </intent-filter>\n        </service>\n\n        <service\n            android:name=\"cards.nine.app.services.sharedcollections.UpdateSharedCollectionService\"\n            android:exported=\"false\"/>\n\n    </application>\n\n    <!--\n        Normal Permissions - Automatically granted\n        https://developer.android.com/guide/topics/security/normal-permissions.html\n    -->\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.VIBRATE\"/>\n    <uses-permission android:name=\"android.permission.BLUETOOTH\" />\n    <!-- Normal Permissions -->\n\n    <!-- Dangerous Permissions - Need user approve -->\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.BIND_APPWIDGET\" />\n    <uses-permission android:name=\"android.permission.CALL_PHONE\"/>\n    <uses-permission android:name=\"android.permission.READ_CALL_LOG\"/>\n    <uses-permission android:name=\"android.permission.READ_CONTACTS\"/>\n    <!-- Dangerous Permissions -->\n\n    <!-- Other permissions -->\n    <uses-permission android:name=\"com.google.android.gms.permission.ACTIVITY_RECOGNITION\" />\n    <uses-permission android:name=\"com.google.android.providers.gsf.permission.READ_GSERVICES\"/>\n    <!-- Other permissions -->\n\n    <!-- Old versions permissions -->\n    <uses-permission android:name=\"android.permission.USE_CREDENTIALS\" />\n    <!-- Old versions permissions -->\n</manifest>"
  },
  {
    "path": "modules/app/src/main/java/cards/nine/utils/SystemBarTintManager.java",
    "content": "/*\n * Copyright (C) 2013 readyState Software Ltd\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.utils;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.res.Configuration;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.Color;\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\nimport android.util.DisplayMetrics;\nimport android.util.TypedValue;\nimport android.view.*;\nimport android.widget.FrameLayout.LayoutParams;\n\n/**\n * Class to manage status and navigation bar tint effects when using KitKat \n * translucent system UI modes.\n *\n */\npublic class SystemBarTintManager {\n\n\t/**\n\t * The default system bar tint color value.\n\t */\n\tpublic static final int DEFAULT_TINT_COLOR = Color.parseColor(\"#99000000\");\n\n\tprivate SystemBarConfig mConfig;\n\tprivate boolean mStatusBarAvailable;\n\tprivate boolean mNavBarAvailable;\n\tprivate boolean mStatusBarTintEnabled;\n\tprivate boolean mNavBarTintEnabled;\n\tprivate View mStatusBarTintView;\n\tprivate View mNavBarTintView;\n\n\t/**\n\t * Constructor. Call this in the host activity onCreate method after its \n\t * content view has been set. You should always create new instances when \n\t * the host activity is recreated.\n\t * \n\t * @param activity The host activity.\n\t */\n\t@TargetApi(19)\n\tpublic SystemBarTintManager(Activity activity) {\n\n\t\tWindow win = activity.getWindow();\n\t\tViewGroup decorViewGroup = (ViewGroup) win.getDecorView();\n\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n\t\t\t// check theme attrs\n\t\t\tint[] attrs = {android.R.attr.windowTranslucentStatus,\n\t\t\t\t\tandroid.R.attr.windowTranslucentNavigation};\n\t\t\tTypedArray a = activity.obtainStyledAttributes(attrs);\n\t\t\tmStatusBarAvailable = a.getBoolean(0, false);\n\t\t\tmNavBarAvailable = a.getBoolean(1, false);\n\n\t\t\t// check window flags\n\t\t\tWindowManager.LayoutParams winParams = win.getAttributes();\n\t\t\tint bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;\n\t\t\tif ((winParams.flags & bits) != 0) {\n\t\t\t\tmStatusBarAvailable = true;\n\t\t\t}\n\t\t\tbits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;\n\t\t\tif ((winParams.flags & bits) != 0) {\n\t\t\t\tmNavBarAvailable = true;\n\t\t\t}\n\t\t}\n\t\t\n\t\tmConfig = new SystemBarConfig(activity, mStatusBarAvailable, mNavBarAvailable);\n\t\t// device might not have virtual navigation keys\n\t\tif (!mConfig.hasNavigationBar()) {\n\t\t\tmNavBarAvailable = false;\n\t\t}\n\t\t\n\t\tif (mStatusBarAvailable) {\n\t\t\tsetupStatusBarView(activity, decorViewGroup);\n\t\t}\n\t\tif (mNavBarAvailable) {\n\t\t\tsetupNavBarView(activity, decorViewGroup);\n\t\t}\n\n\t}\n\n\t/**\n\t * Enable tinting of the system status bar.\n\t * \n\t * If the platform is running Jelly Bean or earlier, or translucent system\n\t * UI modes have not been enabled in either the theme or via window flags,\n\t * then this method does nothing.\n\t * \n\t * @param enabled True to enable tinting, false to disable it (default).\n\t */\n\tpublic void setStatusBarTintEnabled(boolean enabled) {\n\t\tmStatusBarTintEnabled = enabled;\n\t\tif (mStatusBarAvailable) {\n\t\t\tmStatusBarTintView.setVisibility(enabled ? View.VISIBLE : View.GONE);\n\t\t}\n\t}\n\n\t/**\n\t * Enable tinting of the system navigation bar.\n\t * \n\t * If the platform does not have soft navigation keys, is running Jelly Bean \n\t * or earlier, or translucent system UI modes have not been enabled in either \n\t * the theme or via window flags, then this method does nothing.\n\t * \n\t * @param enabled True to enable tinting, false to disable it (default).\n\t */\n\tpublic void setNavigationBarTintEnabled(boolean enabled) {\n\t\tmNavBarTintEnabled = enabled;\n\t\tif (mNavBarAvailable) {\n\t\t\tmNavBarTintView.setVisibility(enabled ? View.VISIBLE : View.GONE);\n\t\t}\n\t}\n\n\t/**\n\t * Apply the specified color tint to all system UI bars.\n\t * \n\t * @param color The color of the background tint.\n\t */\n\tpublic void setTintColor(int color) {\n\t\tsetStatusBarTintColor(color);\n\t\tsetNavigationBarTintColor(color);\n\t}\n\n\t/**\n\t * Apply the specified drawable or color resource to all system UI bars.\n\t * \n\t * @param res The identifier of the resource.\n\t */\n\tpublic void setTintResource(int res) {\n\t\tsetStatusBarTintResource(res);\n\t\tsetNavigationBarTintResource(res);\n\t}\n\n\t/**\n\t * Apply the specified drawable to all system UI bars.\n\t * \n\t * @param drawable The drawable to use as the background, or null to remove it.\n\t */\n\tpublic void setTintDrawable(Drawable drawable) {\n\t\tsetStatusBarTintDrawable(drawable);\n\t\tsetNavigationBarTintDrawable(drawable);\n\t}\n\n\t/**\n\t * Apply the specified color tint to the system status bar.\n\t * \n\t * @param color The color of the background tint.\n\t */\n\tpublic void setStatusBarTintColor(int color) {\n\t\tif (mStatusBarAvailable) {\n\t\t\tmStatusBarTintView.setBackgroundColor(color);\n\t\t}\n\t}\n\n\t/**\n\t * Apply the specified drawable or color resource to the system status bar.\n\t * \n\t * @param res The identifier of the resource.\n\t */\n\tpublic void setStatusBarTintResource(int res) {\n\t\tif (mStatusBarAvailable) {\n\t\t\tmStatusBarTintView.setBackgroundResource(res);\n\t\t}\n\t}\n\n\t/**\n\t * Apply the specified drawable to the system status bar.\n\t * \n\t * @param drawable The drawable to use as the background, or null to remove it.\n\t */\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void setStatusBarTintDrawable(Drawable drawable) {\n\t\tif (mStatusBarAvailable) {\n\t\t\tmStatusBarTintView.setBackgroundDrawable(drawable);\n\t\t}\n\t}\n\n\t/**\n\t * Apply the specified color tint to the system navigation bar.\n\t * \n\t * @param color The color of the background tint.\n\t */\n\tpublic void setNavigationBarTintColor(int color) {\n\t\tif (mNavBarAvailable) {\n\t\t\tmNavBarTintView.setBackgroundColor(color);\n\t\t}\n\t}\n\n\t/**\n\t * Apply the specified drawable or color resource to the system navigation bar.\n\t * \n\t * @param res The identifier of the resource.\n\t */\n\tpublic void setNavigationBarTintResource(int res) {\n\t\tif (mNavBarAvailable) {\n\t\t\tmNavBarTintView.setBackgroundResource(res);\n\t\t}\n\t}\n\n\t/**\n\t * Apply the specified drawable to the system navigation bar.\n\t * \n\t * @param drawable The drawable to use as the background, or null to remove it.\n\t */\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void setNavigationBarTintDrawable(Drawable drawable) {\n\t\tif (mNavBarAvailable) {\n\t\t\tmNavBarTintView.setBackgroundDrawable(drawable);\n\t\t}\n\t}\n\n\t/**\n\t * Get the system bar configuration.\n\t * \n\t * @return The system bar configuration for the current device configuration.\n\t */\n\tpublic SystemBarConfig getConfig() {\n\t\treturn mConfig;\n\t}\n\n\t/**\n\t * Is tinting enabled for the system status bar?\n\t * \n\t * @return True if enabled, False otherwise.\n\t */\n\tpublic boolean isStatusBarTintEnabled() {\n\t\treturn mStatusBarTintEnabled;\n\t}\n\n\t/**\n\t * Is tinting enabled for the system navigation bar?\n\t * \n\t * @return True if enabled, False otherwise.\n\t */\n\tpublic boolean isNavBarTintEnabled() {\n\t\treturn mNavBarTintEnabled;\n\t}\n\n\tprivate void setupStatusBarView(Context context, ViewGroup decorViewGroup) {\n\t\tmStatusBarTintView = new View(context);\n\t\tLayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getStatusBarHeight());\n\t\tparams.gravity = Gravity.TOP;\n\t\tif (mNavBarAvailable && !mConfig.isNavigationAtBottom()) {\n\t\t\tparams.rightMargin = mConfig.getNavigationBarWidth();\n\t\t}\n\t\tmStatusBarTintView.setLayoutParams(params);\n\t\tmStatusBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR);\n\t\tmStatusBarTintView.setVisibility(View.GONE);\n\t\tdecorViewGroup.addView(mStatusBarTintView);\n\t}\n\n\tprivate void setupNavBarView(Context context, ViewGroup decorViewGroup) {\n\t\tmNavBarTintView = new View(context);\n\t\tLayoutParams params;\n\t\tif (mConfig.isNavigationAtBottom()) {\n\t\t\tparams = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getNavigationBarHeight());\n\t\t\tparams.gravity = Gravity.BOTTOM;\n\t\t} else {\n\t\t\tparams = new LayoutParams(mConfig.getNavigationBarWidth(), LayoutParams.MATCH_PARENT);\n\t\t\tparams.gravity = Gravity.RIGHT;\n\t\t}\n\t\tmNavBarTintView.setLayoutParams(params);\n\t\tmNavBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR);\n\t\tmNavBarTintView.setVisibility(View.GONE);\n\t\tdecorViewGroup.addView(mNavBarTintView);\n\t}\n\n\t/**\n\t * Class which describes system bar sizing and other characteristics for the current\n\t * device configuration.\n\t *\n\t */\n\tpublic class SystemBarConfig {\n\n\t\tprivate static final String STATUS_BAR_HEIGHT_RES_NAME = \"status_bar_height\";\n\t\tprivate static final String NAV_BAR_HEIGHT_RES_NAME = \"navigation_bar_height\";\n\t\tprivate static final String NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME = \"navigation_bar_height_landscape\";\n\t\tprivate static final String NAV_BAR_WIDTH_RES_NAME = \"navigation_bar_width\";\n\n\t\tprivate boolean mTranslucentStatusBar;\n\t\tprivate boolean mTranslucentNavBar;\n\t\tprivate int mStatusBarHeight;\n\t\tprivate int mActionBarHeight;\n\t\tprivate boolean mHasNavigationBar;\n\t\tprivate int mNavigationBarHeight;\n\t\tprivate int mNavigationBarWidth;\n\t\tprivate boolean mInPortrait;\n\t\tprivate float mSmallestWidthDp;\n\n\t\tprivate SystemBarConfig(Activity activity, boolean translucentStatusBar, boolean traslucentNavBar) {\n\t\t\tResources res = activity.getResources();\n\t\t\tmInPortrait = (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);\n\t\t\tmSmallestWidthDp = getSmallestWidthDp(activity);\n\t\t\tmStatusBarHeight = getInternalDimensionSize(res, STATUS_BAR_HEIGHT_RES_NAME);\n\t\t\tmActionBarHeight = getActionBarHeight(activity);\n\t\t\tmNavigationBarHeight = getNavigationBarHeight(activity);\n\t\t\tmNavigationBarWidth = getNavigationBarWidth(activity);\n\t\t\tmHasNavigationBar = (mNavigationBarHeight > 0);\n\t\t\tmTranslucentStatusBar = translucentStatusBar;\n\t\t\tmTranslucentNavBar = traslucentNavBar;\n\t\t}\n\n\t\t@TargetApi(14)\n\t\tprivate int getActionBarHeight(Context context) {\n\t\t\tint result = 0;\n\t\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n\t\t\t\tTypedValue tv = new TypedValue();\n\t\t\t\tcontext.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true);\n\t\t\t\tresult = context.getResources().getDimensionPixelSize(tv.resourceId);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t@TargetApi(14)\n\t\tprivate int getNavigationBarHeight(Context context) {\n\t\t\tResources res = context.getResources();\n\t\t\tint result = 0;\n\t\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n\t\t\t\tif (!ViewConfiguration.get(context).hasPermanentMenuKey()) {\n\t\t\t\t\tString key;\n\t\t\t\t\tif (mInPortrait) {\n\t\t\t\t\t\tkey = NAV_BAR_HEIGHT_RES_NAME;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tkey = NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME;\n\t\t\t\t\t}\n\t\t\t\t\treturn getInternalDimensionSize(res, key);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t\t\n\t\t@TargetApi(14)\n\t\tprivate int getNavigationBarWidth(Context context) {\n\t\t\tResources res = context.getResources();\n\t\t\tint result = 0;\n\t\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n\t\t\t\tif (!ViewConfiguration.get(context).hasPermanentMenuKey()) {\n\t\t\t\t\treturn getInternalDimensionSize(res, NAV_BAR_WIDTH_RES_NAME);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t\t\t\n\t\tprivate int getInternalDimensionSize(Resources res, String key) {\n\t\t\tint result = 0;\n\t\t\tint resourceId = res.getIdentifier(key, \"dimen\", \"android\");\n\t\t\tif (resourceId > 0) {\n\t\t\t\tresult = res.getDimensionPixelSize(resourceId);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t@SuppressLint(\"NewApi\")\n\t\tprivate float getSmallestWidthDp(Activity activity) {\n\t\t\tDisplayMetrics metrics = new DisplayMetrics();\n\t\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n\t\t\t\tactivity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);\n\t\t\t} else {\n\t\t\t\t// TODO this is not correct, but we don't really care pre-kitkat\n\t\t\t\tactivity.getWindowManager().getDefaultDisplay().getMetrics(metrics);\n\t\t\t}\n\t\t\tfloat widthDp = metrics.widthPixels / metrics.density;\n\t\t\tfloat heightDp = metrics.heightPixels / metrics.density;\n\t\t\treturn Math.min(widthDp, heightDp);\n\t\t}\n\n\t\t/**\n\t\t * Should a navigation bar appear at the bottom of the screen in the current\n\t\t * device configuration? A navigation bar may appear on the right side of \n\t\t * the screen in certain configurations.\n\t\t * \n\t\t * @return True if navigation should appear at the bottom of the screen, False otherwise.\n\t\t */\n\t\tpublic boolean isNavigationAtBottom() {\n\t\t\treturn (mSmallestWidthDp > 600 || mInPortrait);\n\t\t}\n\n\t\t/**\n\t\t * Get the height of the system status bar.\n\t\t * \n\t\t * @return The height of the status bar (in pixels).\n\t\t */\n\t\tpublic int getStatusBarHeight() {\n\t\t\treturn mStatusBarHeight;\n\t\t}\n\n\t\t/**\n\t\t * Get the height of the action bar.\n\t\t * \n\t\t * @return The height of the action bar (in pixels).\n\t\t */\n\t\tpublic int getActionBarHeight() {\n\t\t\treturn mActionBarHeight;\n\t\t}\n\n\t\t/**\n\t\t * Does this device have a system navigation bar?\n\t\t * \n\t\t * @return True if this device uses soft key navigation, False otherwise.\n\t\t */\n\t\tpublic boolean hasNavigationBar() {\n\t\t\treturn mHasNavigationBar;\n\t\t}\n\n\t\t/**\n\t\t * Get the height of the system navigation bar.\n\t\t * \n\t\t * @return The height of the navigation bar (in pixels). If the device does not have\n\t\t * soft navigation keys, this will always return 0.\n\t\t */\n\t\tpublic int getNavigationBarHeight() {\n\t\t\treturn mNavigationBarHeight;\n\t\t}\n\t\t\n\t\t/**\n\t\t * Get the width of the system navigation bar when it is placed vertically on the screen.\n\t\t * \n\t\t * @return The width of the navigation bar (in pixels). If the device does not have\n\t\t * soft navigation keys, this will always return 0.\n\t\t */\n\t\tpublic int getNavigationBarWidth() {\n\t\t\treturn mNavigationBarWidth;\n\t\t}\n\n\t\t/**\n\t\t * Get the layout inset for any system UI that appears at the top of the screen.\n\t\t * \n\t\t * @param withActionBar True to include the height of the action bar, False otherwise.\n\t\t * @return The layout inset (in pixels).\n\t\t */\n\t\tpublic int getPixelInsetTop(boolean withActionBar) {\n\t\t\treturn (mTranslucentStatusBar ? mStatusBarHeight : 0) + (withActionBar ? mActionBarHeight : 0);\n\t\t}\n\n\t\t/**\n\t\t * Get the layout inset for any system UI that appears at the bottom of the screen.\n\t\t * \n\t\t * @return The layout inset (in pixels).\n\t\t */\n\t\tpublic int getPixelInsetBottom() {\n\t\t\tif (mTranslucentNavBar && isNavigationAtBottom()) {\n\t\t\t\treturn mNavigationBarHeight;\n\t\t\t} else {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Get the layout inset for any system UI that appears at the right of the screen.\n\t\t * \n\t\t * @return The layout inset (in pixels).\n\t\t */\n\t\tpublic int getPixelInsetRight() {\n\t\t\tif (mTranslucentNavBar && !isNavigationAtBottom()) {\n\t\t\t\treturn mNavigationBarWidth;\n\t\t\t} else {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "modules/app/src/main/res/anim/elevation_transition.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_enabled=\"true\" android:state_pressed=\"true\">\n        <objectAnimator\n                android:duration=\"@android:integer/config_shortAnimTime\"\n                android:propertyName=\"translationZ\"\n                android:valueTo=\"@dimen/elevation_pressed\"\n                android:valueType=\"floatType\" />\n    </item>\n    <item>\n        <objectAnimator\n                android:duration=\"@android:integer/config_shortAnimTime\"\n                android:propertyName=\"translationZ\"\n                android:valueTo=\"@dimen/elevation_default\"\n                android:valueType=\"floatType\" />\n    </item>\n</selector>"
  },
  {
    "path": "modules/app/src/main/res/anim/grid_cards_layout_animation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<gridLayoutAnimation\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"@integer/anim_duration_normal\"\n    android:animation=\"@anim/slide_in_bottom\"\n    android:animationOrder=\"normal\"\n    android:columnDelay=\"15%\"\n    android:rowDelay=\"15%\"\n    android:direction=\"top_to_bottom|left_to_right\"/>"
  },
  {
    "path": "modules/app/src/main/res/anim/list_slide_in_bottom_animation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<gridLayoutAnimation\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"@integer/anim_duration_normal\"\n    android:animation=\"@anim/slide_in_bottom\"\n    android:animationOrder=\"normal\"\n    android:columnDelay=\"15%\"\n    android:rowDelay=\"15%\"\n    android:direction=\"top_to_bottom|left_to_right\"/>"
  },
  {
    "path": "modules/app/src/main/res/anim/slide_in_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n           android:interpolator=\"@android:anim/decelerate_interpolator\"\n           android:fromYDelta=\"100%p\" android:toYDelta=\"0\"\n           android:duration=\"@integer/anim_duration_normal\"/>"
  },
  {
    "path": "modules/app/src/main/res/color/wizard_text_button.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:state_enabled=\"false\" android:color=\"@color/wizard_text_button_off\"/>\n    <item android:color=\"@color/wizard_text_button_on\"/>\n\n</selector>"
  },
  {
    "path": "modules/app/src/main/res/drawable/background_icon_collection_detail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:shape=\"oval\">\n\n    <solid android:color=\"@color/background_icon_collection_detail\"/>\n\n</shape>"
  },
  {
    "path": "modules/app/src/main/res/drawable/background_title_fab_menu_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\"\n          android:drawable=\"@drawable/background_title_fab_menu_item_pressed\"/>\n    <item android:drawable=\"@drawable/background_title_fab_menu_item_default\"/>\n</selector>"
  },
  {
    "path": "modules/app/src/main/res/drawable/background_title_fab_menu_item_default.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:shape=\"rectangle\">\n\n    <corners android:radius=\"@dimen/radius_default\" />\n\n    <solid android:color=\"@color/collection_fab_button_item_title_background_default\"/>\n\n</shape>"
  },
  {
    "path": "modules/app/src/main/res/drawable/background_title_fab_menu_item_pressed.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:shape=\"rectangle\">\n\n    <corners android:radius=\"@dimen/radius_default\" />\n\n    <solid android:color=\"@color/collection_fab_button_item_title_background_pressed\"/>\n\n</shape>"
  },
  {
    "path": "modules/app/src/main/res/drawable/drawer_pager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_activated=\"true\"\n          android:drawable=\"@drawable/drawer_pager_current\"/>\n    <item android:drawable=\"@drawable/drawer_pager_default\"/>\n</selector>"
  },
  {
    "path": "modules/app/src/main/res/drawable/drawer_pager_current.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"oval\">\n    <solid android:color=\"@color/drawer_pager_current\" />\n</shape>"
  },
  {
    "path": "modules/app/src/main/res/drawable/drawer_pager_default.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"oval\">\n    <solid android:color=\"@color/drawer_pager_default\" />\n</shape>"
  },
  {
    "path": "modules/app/src/main/res/drawable/fastscroller_bar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"rectangle\">\n\n    <solid android:color=\"@color/primary\" />\n\n    <size\n        android:height=\"@dimen/fastscroller_bar_height\"\n        android:width=\"@dimen/fastscroller_bar_width\" />\n</shape>"
  },
  {
    "path": "modules/app/src/main/res/drawable/fastscroller_signal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"rectangle\">\n\n    <corners\n        android:topLeftRadius=\"@dimen/fastscroller_signal_size\"\n        android:topRightRadius=\"@dimen/fastscroller_signal_size\"\n        android:bottomLeftRadius=\"@dimen/fastscroller_signal_size\"\n        android:bottomRightRadius=\"0dp\" />\n\n    <solid android:color=\"@color/accent\" />\n\n    <size\n        android:height=\"@dimen/fastscroller_signal_size\"\n        android:width=\"@dimen/fastscroller_signal_size\" />\n</shape>"
  },
  {
    "path": "modules/app/src/main/res/drawable/mark_widget_resizing.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"oval\">\n\n    <solid\n        android:color=\"@color/stroke_widget_selected\"/>\n\n</shape>\n\n"
  },
  {
    "path": "modules/app/src/main/res/drawable/publish_collection_wizard_pager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/publish_collection_wizard_pager_current\"/>\n</selector>"
  },
  {
    "path": "modules/app/src/main/res/drawable/publish_collection_wizard_pager_current.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"oval\">\n    <solid android:color=\"@color/publish_collection_wizard_pager_current\" />\n</shape>"
  },
  {
    "path": "modules/app/src/main/res/drawable/stroke_widget_selected.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"rectangle\">\n\n    <stroke\n        android:color=\"@color/stroke_widget_selected\"\n        android:width=\"@dimen/stroke_thin\"/>\n\n</shape>\n\n"
  },
  {
    "path": "modules/app/src/main/res/drawable/wizard_inline_pager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_activated=\"true\"\n          android:drawable=\"@drawable/wizard_inline_pager_current\"/>\n    <item android:drawable=\"@drawable/wizard_inline_pager_default\"/>\n</selector>"
  },
  {
    "path": "modules/app/src/main/res/drawable/wizard_inline_pager_current.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"oval\">\n    <solid android:color=\"@color/wizard_inline_pager_current\" />\n</shape>"
  },
  {
    "path": "modules/app/src/main/res/drawable/wizard_inline_pager_default.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"oval\">\n    <solid android:color=\"@color/wizard_inline_pager_default\" />\n</shape>"
  },
  {
    "path": "modules/app/src/main/res/drawable/wizard_pager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_activated=\"true\"\n          android:drawable=\"@drawable/wizard_pager_current\"/>\n    <item android:drawable=\"@drawable/wizard_pager_default\"/>\n</selector>"
  },
  {
    "path": "modules/app/src/main/res/drawable/wizard_pager_current.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"oval\">\n    <solid android:color=\"@color/wizard_pager_current\" />\n</shape>"
  },
  {
    "path": "modules/app/src/main/res/drawable/wizard_pager_default.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"oval\">\n    <solid android:color=\"@color/wizard_pager_default\" />\n</shape>"
  },
  {
    "path": "modules/app/src/main/res/drawable/workspaces_pager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_activated=\"true\"\n          android:drawable=\"@drawable/ic_pageindicator_current\"/>\n    <item android:drawable=\"@drawable/ic_pageindicator_default\"/>\n</selector>"
  },
  {
    "path": "modules/app/src/main/res/layout/about_header_preference.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/AboutHeaderPreferenceRoot\">\n\n    <ImageView\n        style=\"@style/AboutHeaderPreferenceLogo\"/>\n\n    <TextView\n        style=\"@style/AboutHeaderPreferenceName\"/>\n\n    <TextView\n        android:id=\"@+id/preference_about_name\"\n        style=\"@style/AboutHeaderPreferenceVersion\"/>\n\n    <TextView\n        style=\"@style/AboutHeaderPreferenceText\"/>\n\n    <TextView\n        android:id=\"@+id/preference_about_github\"\n        style=\"@style/AboutHeaderPreferenceGitHub\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/about_team_preference.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<HorizontalScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/AboutTeamPreferenceRoot\">\n\n    <LinearLayout\n        android:id=\"@+id/preference_about_team\"\n        style=\"@style/AboutTeamPreferenceContent\" />\n\n</HorizontalScrollView>"
  },
  {
    "path": "modules/app/src/main/res/layout/add_moment_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/AddMomentRoot\">\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        android:id=\"@+id/add_moment_icon\"\n        style=\"@style/AddMomentIcon\"/>\n\n    <LinearLayout style=\"@style/AddMomentTextContent\">\n\n        <TextView\n            android:id=\"@+id/add_moment_name\"\n            style=\"@style/AddMomentName\"/>\n\n        <TextView\n            android:id=\"@+id/add_moment_description\"\n            style=\"@style/AddMomentDescription\"/>\n\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/app_drawer_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/launcher_drawer_content\"\n    style=\"@style/LauncherDrawerParent\">\n\n    <cards.nine.app.ui.components.layouts.SearchBoxView\n        style=\"@style/LauncherDrawerContent\"\n        android:id=\"@+id/launcher_search_box_content\" />\n\n    <cards.nine.app.ui.components.layouts.FastScrollerLayout\n        android:id=\"@+id/launcher_drawer_scroller_layout\"\n        style=\"@style/LauncherDrawerInnerContent\">\n\n        <TextView\n            android:id=\"@+id/launcher_drawer_message\"\n            style=\"@style/LauncherDrawerMessage\" />\n\n        <cards.nine.app.ui.components.layouts.PullToTabsView\n            android:id=\"@+id/launcher_drawer_pull_to_tabs\"\n            style=\"@style/PullToTabsContent\">\n\n            <cards.nine.app.ui.components.widgets.DrawerRecyclerView\n                android:id=\"@+id/launcher_drawer_recycler\"\n                style=\"@style/PullToTabsRecycler\"/>\n\n        </cards.nine.app.ui.components.layouts.PullToTabsView>\n\n        <LinearLayout\n            android:id=\"@+id/launcher_drawer_tabs\"\n            style=\"@style/PullToTabsItem\"/>\n\n    </cards.nine.app.ui.components.layouts.FastScrollerLayout>\n\n</LinearLayout>\n"
  },
  {
    "path": "modules/app/src/main/res/layout/app_drawer_panel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/LauncherDrawerRoot\">\n\n    <FrameLayout\n        style=\"@style/LauncherDrawerContentItem\">\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            style=\"@style/LauncherDrawerItem\"\n            android:id=\"@+id/launcher_page_1\"/>\n    </FrameLayout>\n\n    <FrameLayout\n        style=\"@style/LauncherDrawerContentItem\">\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            style=\"@style/LauncherDrawerItem\"\n            android:id=\"@+id/launcher_page_2\"/>\n    </FrameLayout>\n\n    <FrameLayout\n        style=\"@style/LauncherDrawerContentItem\">\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            style=\"@style/LauncherDrawerAppsItem\"\n            android:id=\"@+id/launcher_app_drawer\"/>\n    </FrameLayout>\n\n    <FrameLayout\n        style=\"@style/LauncherDrawerContentItem\">\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            style=\"@style/LauncherDrawerItem\"\n            android:id=\"@+id/launcher_page_3\"/>\n    </FrameLayout>\n\n    <FrameLayout\n        style=\"@style/LauncherDrawerContentItem\">\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            style=\"@style/LauncherDrawerItem\"\n            android:id=\"@+id/launcher_page_4\"/>\n    </FrameLayout>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/app_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/AppItemRoot\">\n    \n    <ImageView\n        android:id=\"@+id/simple_item_icon\"\n        style=\"@style/AppItemIcon\"/>\n\n    <TextView\n        android:id=\"@+id/simple_item_name\"\n        style=\"@style/AppItemName\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/app_link_dialog_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/app_link_root\"\n    style=\"@style/AppLinkDialogRoot\">\n\n    <LinearLayout\n        android:id=\"@+id/app_link_loading_layout\"\n        style=\"@style/AppLinkLoadingLayout\">\n\n        <ProgressBar\n            android:id=\"@+id/app_link_loading\"\n            style=\"@style/AppLinkLoading\"/>\n\n        <TextView\n            android:id=\"@+id/app_link_loading_text\"\n            style=\"@style/AppLinkLoadingText\"/>\n\n    </LinearLayout>\n\n    <FrameLayout\n        android:id=\"@+id/app_link_collection\"\n        style=\"@style/AppLinkCollectionLayout\">\n\n        <include\n            layout=\"@layout/public_collections_item\"/>\n\n    </FrameLayout>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/app_select_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             style=\"@style/AppItemRoot\">\n\n    <FrameLayout\n            android:id=\"@+id/app_item_background\"\n            style=\"@style/AppItemBackground\">\n\n        <LinearLayout\n                android:id=\"@+id/app_item_content\"\n                style=\"@style/AppItemContent\">\n\n            <ImageView\n                    android:id=\"@+id/simple_item_icon\"\n                    style=\"@style/AppItemIcon\"/>\n\n            <TextView\n                    android:id=\"@+id/simple_item_name\"\n                    style=\"@style/AppItemName\"/>\n\n        </LinearLayout>\n\n    </FrameLayout>\n\n    <FrameLayout\n            android:id=\"@+id/app_selected_content\"\n            style=\"@style/AppSelectedContent\">\n\n        <ImageView\n            android:id=\"@+id/app_selected\"\n            style=\"@style/AppSelected\" />\n\n    </FrameLayout>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/apps_moment_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/MenuAppsMomentContent\">\n\n    <FrameLayout\n        style=\"@style/MenuAppsMomentIconContent\"\n        android:id=\"@+id/moment_bar_icon_content\">\n\n        <ImageView\n            style=\"@style/MenuAppsMomentIcon\"\n            android:id=\"@+id/moment_bar_icon\"/>\n\n    </FrameLayout>\n\n    <ScrollView style=\"@style/MenuAppsMomentScroll\">\n\n        <LinearLayout\n            style=\"@style/MenuAppsMomentApps\"\n            android:id=\"@+id/moment_bar_apps\"/>\n\n    </ScrollView>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/base_action_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             style=\"@style/BaseActionsRoot\">\n\n    <LinearLayout\n        android:id=\"@+id/action_content_root\"\n        style=\"@style/BaseActionsContent\">\n\n        <cards.nine.app.ui.components.layouts.DialogToolbar\n            android:id=\"@+id/actions_toolbar\"\n            style=\"@style/BaseActionsToolbarDialog\"/>\n\n        <FrameLayout\n            style=\"@style/BaseActionsContentRoot\">\n\n            <LinearLayout\n                android:id=\"@+id/action_loading\"\n                style=\"@style/BaseActionsLoadingContent\">\n\n                <TextView\n                    android:id=\"@+id/action_loading_text\"\n                    style=\"@style/BaseActionsLoadingText\"/>\n\n                <ProgressBar\n                    android:id=\"@+id/action_loading_bar\"\n                    style=\"@style/BaseActionsLoading\"/>\n\n            </LinearLayout>\n\n            <FrameLayout\n                android:id=\"@+id/action_content_layout\"\n                style=\"@style/BaseActionsContentLayout\"/>\n\n            <LinearLayout\n                android:id=\"@+id/actions_content_error_layout\"\n                style=\"@style/BaseActionsErrorContent\">\n\n                <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/actions_content_error_icon\"\n                    style=\"@style/BaseActionsErrorIcon\"/>\n\n                <TextView\n                    android:id=\"@+id/actions_content_error_message\"\n                    style=\"@style/BaseActionsErrorMessage\"/>\n\n                <android.support.v7.widget.AppCompatButton\n                    android:id=\"@+id/actions_content_error_button\"\n                    style=\"@style/BaseActionsErrorButton\"/>\n\n            </LinearLayout>\n\n        </FrameLayout>\n\n    </LinearLayout>\n\n    <android.support.design.widget.FloatingActionButton\n        style=\"@style/BaseActionsFabButton\"\n        android:id=\"@+id/action_content_fab\"/>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/card_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.v7.widget.CardView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/CardRoot\">\n\n    <LinearLayout\n        style=\"@style/CardContent\">\n\n        <FrameLayout\n            android:id=\"@+id/card_icon_content\"\n            style=\"@style/CardIconContent\">\n\n            <cards.nine.app.ui.components.widgets.TintableImageView\n                android:id=\"@+id/card_icon\"\n                style=\"@style/CardIcon\"/>\n\n        </FrameLayout>\n\n        <TextView\n            android:id=\"@+id/card_text\"\n            style=\"@style/CardText\"/>\n\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/card_selected\"\n        style=\"@style/CardSelected\"/>\n\n    <ImageView\n        android:id=\"@+id/card_badge\"\n        style=\"@style/CardBadge\"/>\n\n</android.support.v7.widget.CardView>"
  },
  {
    "path": "modules/app/src/main/res/layout/collection_bar_view_panel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/LauncherCollectionsContent\">\n\n    <LinearLayout\n        style=\"@style/LauncherCollectionsPanel\"\n        android:id=\"@+id/launcher_search_panel\">\n\n        <ImageView\n            style=\"@style/LauncherCollectionsBurgerButton\"\n            android:id=\"@+id/launcher_burger_icon\"/>\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            style=\"@style/LauncherCollectionsGoogleButton\"\n            android:id=\"@+id/launcher_google_icon\"/>\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            style=\"@style/LauncherCollectionsMicButton\"\n            android:id=\"@+id/launcher_mic_icon\"/>\n\n    </LinearLayout>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/collection_checkbox.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             android:id=\"@+id/collection_check_content\"\n             style=\"@style/SubscriptionsCollectionCheckBox\">\n\n    <ImageView\n            android:id=\"@+id/collection_icon\"\n            style=\"@style/SubscriptionsItemIcon\"/>\n\n    <FrameLayout\n            android:id=\"@+id/subscriptions_item_content\"\n            style=\"@style/SubscriptionsCollectionCheckBoxIconContent\">\n\n        <ImageView\n                android:id=\"@+id/collection_checkbox_icon\"\n                style=\"@style/SubscriptionsItemCheckboxIcon\"/>\n\n    </FrameLayout>\n\n</FrameLayout>\n"
  },
  {
    "path": "modules/app/src/main/res/layout/collection_detail_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             android:id=\"@+id/collection_detail_layout\"\n             style=\"@style/CollectionDetailLayout\">\n\n    <android.support.v7.widget.CardView\n            android:id=\"@+id/collection_detail_empty\"\n            style=\"@style/CollectionDetailEmpty\">\n\n        <LinearLayout\n                android:id=\"@+id/collection_detail_empty_content\"\n                style=\"@style/CollectionDetailEmptyContent\">\n\n            <TextView\n                    android:id=\"@+id/collection_empty_message\"\n                    style=\"@style/CollectionEmptyMessage\"/>\n\n            <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/collection_empty_image\"\n                    style=\"@style/CollectionEmptyImage\"/>\n        </LinearLayout>\n\n    </android.support.v7.widget.CardView>\n\n    <cards.nine.app.ui.components.layouts.PullToCloseView\n            android:id=\"@+id/collection_detail_pull_to_close\"\n            style=\"@style/CollectionDetailPullToClose\">\n\n        <cards.nine.app.ui.components.widgets.CollectionRecyclerView\n                android:id=\"@+id/collection_detail_recycler\"\n                style=\"@style/CollectionDetailView\"/>\n\n    </cards.nine.app.ui.components.layouts.PullToCloseView>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/collection_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/LauncherCollectionItemRoot\">\n\n    <LinearLayout\n        android:id=\"@+id/launcher_collection_item_layout\"\n        style=\"@style/LauncherCollectionItemLayout\">\n\n        <FrameLayout\n            android:id=\"@+id/launcher_collection_item_icon_root\"\n            style=\"@style/LauncherCollectionItemIconRoot\">\n\n            <ImageView\n                android:id=\"@+id/launcher_collection_item_icon\"\n                style=\"@style/LauncherCollectionItemIcon\"/>\n\n        </FrameLayout>\n\n        <TextView\n            android:id=\"@+id/launcher_collection_item_name\"\n            style=\"@style/LauncherCollectionItemName\"/>\n\n    </LinearLayout>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/collections_actions_view_panel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/LauncherCollectionActionsRoot\">\n\n    <FrameLayout\n        android:id=\"@+id/launcher_collections_content_1\"\n        style=\"@style/LauncherCollectionActionLayout\">\n\n        <cards.nine.app.ui.components.widgets.TintableButton\n            style=\"@style/LauncherCollectionAction\"\n            android:id=\"@+id/launcher_collections_action_1\"/>\n\n    </FrameLayout>\n\n    <FrameLayout\n        android:id=\"@+id/launcher_collections_content_2\"\n        style=\"@style/LauncherCollectionActionLayout\">\n\n        <cards.nine.app.ui.components.widgets.TintableButton\n            style=\"@style/LauncherCollectionAction\"\n            android:id=\"@+id/launcher_collections_action_2\"/>\n\n    </FrameLayout>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/collections_detail_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n             android:id=\"@+id/collections_root\"\n             style=\"@style/CollectionsRoot\">\n\n    <android.support.v4.view.ViewPager\n        style=\"@style/CollectionsPager\"\n        android:id=\"@+id/collections_view_pager\"/>\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/collections_toolbar\"\n        android:theme=\"@style/ThemeOverlay.AppCompat.Dark.ActionBar\"\n        app:popupTheme=\"@style/ThemeOverlay.AppCompat.Light\"\n        style=\"@style/CollectionsToolbar\">\n\n        <TextView\n            android:id=\"@+id/collections_toolbar_title\"\n            style=\"@style/CollectionsToolbarTitle\" />\n\n    </android.support.v7.widget.Toolbar>\n\n    <FrameLayout\n        android:id=\"@+id/collections_icon_content\"\n        style=\"@style/CollectionsIconContent\">\n\n        <ImageView\n            android:id=\"@+id/collections_icon\"\n            style=\"@style/CollectionsIcon\"/>\n\n    </FrameLayout>\n\n    <cards.nine.app.ui.components.layouts.SlidingTabLayout\n        android:id=\"@+id/collections_tabs\"\n        style=\"@style/CollectionsTabs\"/>\n\n    <FrameLayout\n        android:id=\"@+id/fab_menu_content\"\n        style=\"@style/CollectionsFabMenuContent\">\n\n        <LinearLayout\n            android:id=\"@+id/fab_menu\"\n            style=\"@style/FabMenu\">\n\n            <android.support.design.widget.FloatingActionButton\n                style=\"@style/CollectionsFabButton\"\n                android:id=\"@+id/fab_button\"/>\n\n        </LinearLayout>\n\n    </FrameLayout>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/collections_detail_tab.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView style=\"@style/CollectionsTab\" />"
  },
  {
    "path": "modules/app/src/main/res/layout/collections_workspace_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        style=\"@style/LauncherCollectionsRoot\">\n\n    <GridLayout\n            android:id=\"@+id/launcher_collections_grid\"\n            style=\"@style/LauncherCollectionsGrid\"/>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/color_info_item_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/DialogColorInfoRoot\">\n\n    <ImageView\n        android:id=\"@+id/color_info_image\"\n        style=\"@style/DialogColorInfoImage\"/>\n\n</FrameLayout>\n"
  },
  {
    "path": "modules/app/src/main/res/layout/contact_info_email_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             style=\"@style/DialogContactInfoRoot\">\n\n    <LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                  style=\"@style/DialogContactInfoEmailContent\"\n                  android:id=\"@+id/contact_dialog_email_content\">\n\n            <cards.nine.app.ui.components.widgets.TintableImageView\n                    style=\"@style/DialogContactInfoEmailIcon\"\n                    android:id=\"@+id/contact_dialog_email_icon\"/>\n\n\n            <LinearLayout style=\"@style/DialogContactInfoEmailContentValues\">\n\n                <TextView\n                        style=\"@style/DialogContactInfoValue\"\n                        android:id=\"@+id/contact_dialog_email_address\"/>\n\n                <TextView\n                        style=\"@style/DialogContactInfoCategory\"\n                        android:id=\"@+id/contact_dialog_email_category\"/>\n\n            </LinearLayout>\n\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/contact_dialog_email_line\"\n        style=\"@style/DialogContactInfoLineHorizontal\"/>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/contact_info_general_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             style=\"@style/DialogContactInfoRoot\">\n\n    <LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                  style=\"@style/DialogContactInfoGeneralContent\"\n                  android:id=\"@+id/contact_dialog_general_content\">\n\n            <FrameLayout style=\"@style/DialogContactInfoContentGeneralIcon\">\n\n                <ImageView\n                        style=\"@style/DialogContactInfoGeneralIcon\"\n                        android:id=\"@+id/contact_dialog_general_icon\"/>\n\n            </FrameLayout>\n\n            <LinearLayout style=\"@style/DialogContactInfoGeneralContentValues\">\n\n                <TextView\n                        style=\"@style/DialogContactInfoValue\"\n                        android:id=\"@+id/contact_dialog_general_info\"/>\n\n            </LinearLayout>\n\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/contact_dialog_general_line\"\n        style=\"@style/DialogContactInfoLineHorizontal\"/>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/contact_info_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/ContactInfoHeaderRoot\">\n\n    <ImageView\n            android:id=\"@+id/contact_info_header_avatar\"\n            style=\"@style/ContactInfoHeaderAvatar\"/>\n\n    <LinearLayout\n        style=\"@style/ContactInfoHeaderContent\"\n        android:id=\"@+id/contact_info_header\">\n\n        <TextView\n            android:id=\"@+id/contact_info_header_name\"\n            style=\"@style/ContactInfoHeaderName\"/>\n\n    </LinearLayout>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/contact_info_phone_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             style=\"@style/DialogContactInfoRoot\">\n    \n    <LinearLayout \n                  style=\"@style/DialogContactInfoContentValues\">\n\n        <LinearLayout\n                style=\"@style/DialogContactInfoPhoneContent\"\n                android:id=\"@+id/contact_dialog_phone_content\">\n\n            <cards.nine.app.ui.components.widgets.TintableImageView\n                    style=\"@style/DialogContactInfoPhoneIcon\"\n                    android:id=\"@+id/contact_dialog_phone_icon\"/>\n\n            <LinearLayout style=\"@style/DialogContactInfoPhoneContentValues\">\n\n                <TextView\n                        style=\"@style/DialogContactInfoValue\"\n                        android:id=\"@+id/contact_dialog_phone_number\"/>\n\n                <TextView\n                        style=\"@style/DialogContactInfoCategory\"\n                        android:id=\"@+id/contact_dialog_phone_category\"/>\n            </LinearLayout>\n\n        </LinearLayout>\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n                style=\"@style/DialogContactInfoSmsIcon\"\n                android:id=\"@+id/contact_dialog_sms_icon\"/>\n\n    \n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/contact_dialog_phone_line\"\n        style=\"@style/DialogContactInfoLineHorizontal\"/>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/contact_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/ContactItemRoot\">\n\n    <FrameLayout style=\"@style/ContactItemContentIcon\">\n\n        <ImageView\n            android:id=\"@+id/contact_item_icon\"\n            style=\"@style/ContactItemIcon\"/>\n\n        <ImageView\n                android:id=\"@+id/contact_item_favorite\"\n                style=\"@style/ContactItemFavorite\"/>\n\n    </FrameLayout>\n\n    <TextView\n        android:id=\"@+id/contact_item_name\"\n        style=\"@style/ContactItemName\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/dialog_edit_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/ProfileConfigurationNameDialog\">\n\n    <android.support.design.widget.TextInputLayout\n        style=\"@style/EditCardNameWrapper\">\n\n        <EditText\n            android:id=\"@+id/dialog_edit_card_name\"\n            style=\"@style/EditCardName\"/>\n\n    </android.support.design.widget.TextInputLayout>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/dialog_edit_text.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/ProfileConfigurationNameDialog\">\n\n    <android.support.design.widget.TextInputLayout\n        style=\"@style/ProfileConfigurationNameWrapper\">\n\n        <EditText\n            android:id=\"@+id/dialog_edittext\"\n            style=\"@style/ProfileConfigurationName\"/>\n\n    </android.support.design.widget.TextInputLayout>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/edit_moment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/EditMomentScroll\">\n\n    <LinearLayout\n        style=\"@style/EditMomentRoot\">\n\n        <LinearLayout\n            android:id=\"@+id/edit_moment_link_root\"\n            style=\"@style/EditMomentItem\">\n\n            <LinearLayout\n                style=\"@style/EditMomentHeaderContent\">\n\n                <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/edit_moment_icon_link_collection\"\n                    style=\"@style/EditMomentHeaderIconCollection\"/>\n\n                <TextView\n                    android:id=\"@+id/edit_moment_name_link_collection\"\n                    style=\"@style/EditMomentHeaderCollectionName\"/>\n\n                <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/edit_moment_collection_info\"\n                    style=\"@style/EditMomentHeaderActionInfo\"/>\n\n            </LinearLayout>\n\n            <ImageView\n                style=\"@style/ActionsLine\"/>\n\n            <TextView\n                android:id=\"@+id/edit_moment_collection\"\n                style=\"@style/EditMomentHeaderSelectCollection\"/>\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/edit_moment_message_root\"\n            style=\"@style/EditMomentItem\">\n\n            <LinearLayout\n                style=\"@style/EditMomentHeaderContent\">\n\n                <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/edit_moment_icon_message\"\n                    style=\"@style/EditMomentHeaderIconCollection\"/>\n\n                <TextView\n                    android:id=\"@+id/edit_moment_name_message\"\n                    style=\"@style/EditMomentHeaderSpecificMessageName\"/>\n\n            </LinearLayout>\n\n            <ImageView\n                style=\"@style/ActionsLine\"/>\n\n            <TextView\n                android:id=\"@+id/edit_moment_message\"\n                style=\"@style/EditMomentHeaderSpecificMessage\"/>\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/edit_moment_hours_root\"\n            style=\"@style/EditMomentItem\">\n\n            <LinearLayout\n                style=\"@style/EditMomentHeaderContent\">\n\n                <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/edit_moment_icon_hour\"\n                    style=\"@style/EditMomentHeaderIconHours\"/>\n\n                <TextView\n                    android:id=\"@+id/edit_moment_name_hour\"\n                    style=\"@style/EditMomentHeaderHoursName\"/>\n\n                <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/edit_moment_add_hour\"\n                    style=\"@style/EditMomentHeaderActionAdd\"/>\n\n            </LinearLayout>\n\n            <ImageView\n                style=\"@style/ActionsLine\"/>\n\n            <LinearLayout\n                android:id=\"@+id/edit_moment_hour_content\"\n                style=\"@style/EditMomentBodyContent\"/>\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/edit_moment_wifi_root\"\n            style=\"@style/EditMomentItem\">\n\n            <LinearLayout\n                style=\"@style/EditMomentHeaderContent\">\n\n                <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/edit_moment_icon_wifi\"\n                    style=\"@style/EditMomentHeaderIconWifi\"/>\n\n                <TextView\n                    android:id=\"@+id/edit_moment_name_wifi\"\n                    style=\"@style/EditMomentHeaderWifiName\"/>\n\n                <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/edit_moment_add_wifi\"\n                    style=\"@style/EditMomentHeaderActionAdd\"/>\n\n            </LinearLayout>\n\n            <ImageView\n                style=\"@style/ActionsLine\"/>\n\n            <LinearLayout\n                android:id=\"@+id/edit_moment_wifi_content\"\n                style=\"@style/EditMomentBodyContent\"/>\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/edit_moment_bluetooth_root\"\n            style=\"@style/EditMomentItem\">\n\n            <LinearLayout\n                style=\"@style/EditMomentHeaderContent\">\n\n                <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/edit_moment_icon_bluetooth\"\n                    style=\"@style/EditMomentHeaderIconBluetooth\"/>\n\n                <TextView\n                    android:id=\"@+id/edit_moment_name_bluetooth\"\n                    style=\"@style/EditMomentHeaderBluetoothName\"/>\n\n                <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/edit_moment_add_bluetooth\"\n                    style=\"@style/EditMomentHeaderActionAdd\"/>\n\n            </LinearLayout>\n\n            <ImageView\n                style=\"@style/ActionsLine\"/>\n\n            <LinearLayout\n                android:id=\"@+id/edit_moment_bluetooth_content\"\n                style=\"@style/EditMomentBodyContent\"/>\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n</ScrollView>"
  },
  {
    "path": "modules/app/src/main/res/layout/edit_moment_hour_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/EditHourMomentRoot\">\n\n    <LinearLayout\n        style=\"@style/EditHourMomentContent\">\n\n        <LinearLayout\n            style=\"@style/EditHourMomentHoursLine\">\n\n            <FrameLayout\n                android:id=\"@+id/edit_hour_start_content\"\n                style=\"@style/EditHourMomentHourContent\">\n\n                <TextView\n                    android:id=\"@+id/edit_hour_start_text\"\n                    style=\"@style/EditHourMomentHourText\"/>\n\n                <ImageView\n                    style=\"@style/EditHourMomentHourLine\"/>\n\n            </FrameLayout>\n\n            <FrameLayout\n                android:id=\"@+id/edit_hour_end_content\"\n                style=\"@style/EditHourMomentHourContent\">\n\n                <TextView\n                    android:id=\"@+id/edit_hour_end_text\"\n                    style=\"@style/EditHourMomentHourText\"/>\n\n                <ImageView\n                    style=\"@style/EditHourMomentHourLine\"/>\n\n            </FrameLayout>\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/edit_hour_days_content\"\n            style=\"@style/EditHourMomentDaysContent\" />\n\n    </LinearLayout>\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        android:id=\"@+id/edit_hour_action_delete\"\n        style=\"@style/ActionsActionDelete\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/edit_moment_wifi_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/EditWifiMomentRoot\">\n\n    <LinearLayout\n        style=\"@style/EditWifiMomentWifiLine\">\n\n        <TextView\n            android:id=\"@+id/edit_wifi_name\"\n            style=\"@style/EditWifiMomentWifiText\"/>\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            android:id=\"@+id/edit_wifi_action_delete\"\n            style=\"@style/ActionsActionDelete\"/>\n\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/empty_profile_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/profile_empty_item\"\n    style=\"@style/BaseActionsErrorContent\">\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n            android:id=\"@+id/profile_empty_image\"\n            style=\"@style/BaseActionsErrorIcon\"/>\n\n    <TextView\n            android:id=\"@+id/profile_empty_message\"\n            style=\"@style/BaseActionsErrorMessage\"/>\n\n    <android.support.v7.widget.AppCompatButton\n            android:id=\"@+id/profile_empty_button\"\n            style=\"@style/BaseActionsErrorButton\"/>\n\n</LinearLayout>\n"
  },
  {
    "path": "modules/app/src/main/res/layout/fab_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/FabMenuItem\">\n\n    <TextView\n        android:id=\"@+id/fab_title\"\n        style=\"@style/FabMenuItemTitle\"/>\n\n    <ImageView\n        android:id=\"@+id/fab_icon\"\n        style=\"@style/FabMenuItemIcon\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/fastscroller.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:layout_width=\"wrap_content\"\n       android:layout_height=\"match_parent\">\n\n    <FrameLayout\n        android:id=\"@+id/fastscroller_signal\"\n        style=\"@style/FastScrollerSignal\">\n\n        <TextView\n            android:id=\"@+id/fastscroller_signal_text\"\n            style=\"@style/FastScrollerText\"/>\n\n        <TextView\n            android:id=\"@+id/fastscroller_signal_icon\"\n            style=\"@style/FastScrollerIcon\"/>\n\n    </FrameLayout>\n\n    <FrameLayout\n        android:id=\"@+id/fastscroller_bar_content\"\n        style=\"@style/FastScrollerBarContent\">\n\n        <ImageView\n            android:id=\"@+id/fastscroller_bar\"\n            style=\"@style/FastScrollerBar\" />\n\n    </FrameLayout>\n\n</merge>"
  },
  {
    "path": "modules/app/src/main/res/layout/header_list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/HeaderListRoot\">\n\n    <TextView\n        android:id=\"@+id/simple_category_name\"\n        style=\"@style/HeaderListName\"/>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/icon_info_item_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/DialogIconInfoRoot\">\n\n    <TextView\n        style=\"@style/DialogIconInfoName\"\n        android:id=\"@+id/icon_dialog_name\"/>\n\n    <ImageView\n        android:id=\"@+id/icon_dialog_select\"\n        style=\"@style/DialogIconInfoSelect\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/last_call_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/LastCallItemRoot\">\n    \n    <ImageView\n        android:id=\"@+id/last_call_item_icon\"\n        style=\"@style/LastCallItemIcon\"/>\n\n    <LinearLayout style=\"@style/LastCallContentInfo\">\n\n        <TextView\n            android:id=\"@+id/last_call_item_name\"\n            style=\"@style/LastCallItemName\"/>\n\n        <LinearLayout style=\"@style/LastCallContentTime\">\n\n            <LinearLayout\n                android:id=\"@+id/last_call_item_types\"\n                style=\"@style/LastCallContentCalls\"/>\n\n            <TextView\n                android:id=\"@+id/last_call_item_hour\"\n                style=\"@style/LastCallItemHour\"/>\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/launcher_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<android.support.v4.widget.DrawerLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/launcher_drawer_layout\"\n    style=\"@style/MenuDrawerLayout\">\n\n    <FrameLayout\n        android:id=\"@+id/launcher_root\"\n        style=\"@style/LauncherRoot\">\n\n        <ProgressBar\n            style=\"@style/LauncherLoading\"\n            android:id=\"@+id/launcher_loading\"/>\n\n        <LinearLayout\n            style=\"@style/LauncherRootContent\"\n            android:id=\"@+id/launcher_content\">\n\n            <FrameLayout\n                style=\"@style/LauncherTopBox\">\n\n                <cards.nine.app.ui.components.layouts.CollectionActionsPanelLayout\n                    style=\"@style/LauncherCollectionActionsPanel\"\n                    android:id=\"@+id/launcher_collections_actions_panel\"/>\n\n                <cards.nine.app.ui.components.layouts.TopBarLayout\n                    style=\"@style/LauncherSearchPanel\"\n                    android:id=\"@+id/launcher_top_bar_panel\"/>\n\n            </FrameLayout>\n\n            <FrameLayout\n                style=\"@style/LauncherWorkSpacesLayout\">\n\n                <cards.nine.app.ui.components.layouts.LauncherWorkSpaces\n                    style=\"@style/LauncherWorkSpaces\"\n                    android:id=\"@+id/launcher_work_spaces\"/>\n\n                <ImageView\n                    style=\"@style/LauncherWorkSpacesEdgeLeft\"\n                    android:id=\"@+id/launcher_work_spaces_edge_left\"/>\n\n                <ImageView\n                    style=\"@style/LauncherWorkSpacesEdgeRight\"\n                    android:id=\"@+id/launcher_work_spaces_edge_right\"/>\n\n            </FrameLayout>\n\n            <LinearLayout\n                style=\"@style/LauncherPaginationPanel\"\n                android:id=\"@+id/launcher_pagination_panel\"/>\n\n            <cards.nine.app.ui.components.layouts.DockAppsPanelLayout\n                android:id=\"@+id/launcher_dock_apps_panel\"\n                style=\"@style/LauncherDrawerPanel\"/>\n\n        </LinearLayout>\n\n        <include layout=\"@layout/workspace_menu_layout\"/>\n\n        <include layout=\"@layout/app_drawer_layout\"/>\n\n        <FrameLayout\n            style=\"@style/LauncherForeground\"\n            android:id=\"@+id/launcher_foreground\"/>\n\n    </FrameLayout>\n\n    <android.support.design.widget.NavigationView\n        android:id=\"@+id/launcher_navigation_view\"\n        app:headerLayout=\"@layout/menu_header\"\n        app:menu=\"@menu/app_menu\"\n        style=\"@style/MenuNavigationView\"/>\n\n    <cards.nine.app.ui.components.layouts.AppsMomentLayout\n        android:id=\"@+id/launcher_apps_moment\"\n        style=\"@style/MenuAppsMomentContent\"/>\n\n</android.support.v4.widget.DrawerLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/list_action_apps_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/AddAppsActionsRoot\">\n\n    <LinearLayout\n        android:id=\"@+id/selected_apps_content\"\n        style=\"@style/AddAppsSelectedMessageContent\">\n\n        <TextView\n            android:id=\"@+id/selected_apps\"\n            style=\"@style/AddAppsSelectedMessage\"/>\n\n    </LinearLayout>\n\n    <FrameLayout\n        android:id=\"@+id/apps_action_scroller_layout\"\n        style=\"@style/ActionsListContent\">\n\n        <TextView\n            android:id=\"@+id/apps_action_message\"\n            style=\"@style/AddAppsMessage\"/>\n\n        <android.support.v7.widget.RecyclerView\n            android:id=\"@+id/apps_actions_recycler\"\n            style=\"@style/AddAppsActionsRecycler\"/>\n\n    </FrameLayout>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/list_action_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<android.support.v7.widget.RecyclerView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/actions_recycler\"\n    style=\"@style/ActionsRecycler\"/>"
  },
  {
    "path": "modules/app/src/main/res/layout/list_action_with_scroller_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<cards.nine.app.ui.components.layouts.FastScrollerLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/action_scroller_layout\"\n    style=\"@style/ActionsListContent\">\n\n    <cards.nine.app.ui.components.layouts.PullToTabsView\n        android:id=\"@+id/actions_pull_to_tabs\"\n        style=\"@style/PullToTabsContent\">\n\n        <android.support.v7.widget.RecyclerView\n            android:id=\"@+id/actions_recycler\"\n            style=\"@style/PullToTabsRecycler\"/>\n\n    </cards.nine.app.ui.components.layouts.PullToTabsView>\n\n    <LinearLayout\n        android:id=\"@+id/actions_tabs\"\n        style=\"@style/PullToTabsItem\"/>\n\n</cards.nine.app.ui.components.layouts.FastScrollerLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/list_item_popup_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView style=\"@style/ListPopupMenuText\" />"
  },
  {
    "path": "modules/app/src/main/res/layout/menu_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/menu_header\"\n    style=\"@style/MenuHeaderRoot\">\n\n    <ImageView\n            android:id=\"@+id/menu_cover\"\n            style=\"@style/MenuHeaderCover\"/>\n\n    <LinearLayout\n        style=\"@style/MenuHeaderContent\">\n\n        <ImageView\n            android:id=\"@+id/menu_avatar\"\n            style=\"@style/MenuHeaderAvatar\"/>\n\n        <TextView\n            android:id=\"@+id/menu_name\"\n            style=\"@style/MenuHeaderName\"/>\n\n        <TextView\n            android:id=\"@+id/menu_email\"\n            style=\"@style/MenuHeaderEmail\"/>\n\n\n    </LinearLayout>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/moment_bar_view_panel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/LauncherMomentPanel\"\n    android:id=\"@+id/launcher_moment_panel\">\n\n    <LinearLayout\n        android:id=\"@+id/launcher_moment_content\"\n        style=\"@style/LauncherMomentInfoContent\">\n\n        <FrameLayout\n            android:id=\"@+id/launcher_moment_icon_content\"\n            style=\"@style/LauncherMomentIconContent\">\n\n            <cards.nine.app.ui.components.widgets.TintableImageView\n                style=\"@style/LauncherMomentIcon\"\n                android:id=\"@+id/launcher_moment_icon\"/>\n\n        </FrameLayout>\n\n        <TextView\n            style=\"@style/LauncherMomentText\"\n            android:id=\"@+id/launcher_moment_text\"/>\n\n    </LinearLayout>\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        style=\"@style/LauncherMomentUnpin\"\n        android:id=\"@+id/launcher_moment_unpin\"/>\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        style=\"@style/LauncherMomentWeather\"\n        android:id=\"@+id/launcher_moment_weather\"/>\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        style=\"@style/LauncherMomentGoogleButton\"\n        android:id=\"@+id/launcher_moment_google_icon\"/>\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        style=\"@style/LauncherMomentMicButton\"\n        android:id=\"@+id/launcher_moment_mic_icon\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/new_collection.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/NewCollectionRoot\">\n\n    <EditText\n        style=\"@style/NewCollectionName\"\n        android:id=\"@+id/new_collection_name\"\n        android:inputType=\"text\"/>\n\n    <ImageView\n        style=\"@style/ActionsLine\"/>\n\n    <LinearLayout\n        android:id=\"@+id/new_collection_select_color_content\"\n        style=\"@style/NewCollectionSelectorContent\">\n\n        <ImageView\n            android:id=\"@+id/new_collection_select_color_image\"\n            style=\"@style/NewCollectionSelectorIcon\"/>\n\n        <TextView\n            android:id=\"@+id/new_collection_select_color_text\"\n            android:text=\"@string/selectColor\"\n            style=\"@style/NewCollectionSelectorTitle\"/>\n\n    </LinearLayout>\n\n    <ImageView\n        style=\"@style/ActionsLine\"/>\n\n    <LinearLayout\n        android:id=\"@+id/new_collection_select_icon_content\"\n        style=\"@style/NewCollectionSelectorContent\">\n\n        <ImageView\n            android:id=\"@+id/new_collection_select_icon_image\"\n            style=\"@style/NewCollectionSelectorIcon\"/>\n\n        <TextView\n            android:id=\"@+id/new_collection_select_icon_text\"\n            android:text=\"@string/selectIcon\"\n            style=\"@style/NewCollectionSelectorTitle\"/>\n\n    </LinearLayout>\n\n    <ImageView\n        style=\"@style/ActionsLine\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/private_collections_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<android.support.v7.widget.CardView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/private_collections_item_layout\"\n    style=\"@style/PrivateCollectionsItemRoot\">\n\n    <LinearLayout\n        style=\"@style/PrivateCollectionsItemContent\">\n\n        <LinearLayout\n            style=\"@style/PrivateCollectionsItemHeader\">\n\n            <FrameLayout\n                style=\"@style/PrivateCollectionsItemHeaderIconContent\"\n                android:id=\"@+id/private_collections_item_content\">\n\n                <ImageView\n                    android:id=\"@+id/private_collections_item_icon\"\n                    style=\"@style/PrivateCollectionsItemHeaderIcon\"/>\n\n            </FrameLayout>\n\n            <TextView\n                android:id=\"@+id/private_collections_item_name\"\n                style=\"@style/PrivateCollectionsItemHeaderName\"/>\n\n        </LinearLayout>\n\n        <com.google.android.flexbox.FlexboxLayout\n                android:id=\"@+id/private_collections_item_row\"\n                style=\"@style/PublicCollectionsItemIconsApps\"/>\n\n        <ImageView\n            android:id=\"@+id/private_collections_item_line\"\n            style=\"@style/PrivateCollectionsItemLine\"/>\n\n        <Button\n            android:id=\"@+id/private_collections_item_add_collection\"\n            style=\"@style/PrivateCollectionsItemAddCollection\"/>\n\n    </LinearLayout>\n\n</android.support.v7.widget.CardView>\n"
  },
  {
    "path": "modules/app/src/main/res/layout/profile_account_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/ProfileAccountsItemLayout\">\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        android:id=\"@+id/profile_account_device\"\n        style=\"@style/ProfileAccountsItemIcon\"/>\n\n    <LinearLayout\n        style=\"@style/ProfileAccountsItemLayoutData\">\n\n        <TextView\n            android:id=\"@+id/profile_account_title\"\n            style=\"@style/ProfileAccountsItemTitle\"/>\n        <TextView\n            android:id=\"@+id/profile_account_subtitle\"\n            style=\"@style/ProfileAccountsItemSubtitle\"/>\n\n    </LinearLayout>\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        android:id=\"@+id/profile_account_action\"\n        style=\"@style/ProfileAccountsItemAction\"/>\n\n</LinearLayout>\n"
  },
  {
    "path": "modules/app/src/main/res/layout/profile_account_item_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/title\"\n    style=\"@style/ProfileAccountsItemHeader\"/>"
  },
  {
    "path": "modules/app/src/main/res/layout/profile_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/profile_root\"\n    style=\"@style/ProfileRoot\"\n    tools:ignore=\"RtlHardcoded\">\n\n    <android.support.design.widget.CoordinatorLayout\n        style=\"@style/ProfileCoordinator\">\n\n        <android.support.design.widget.AppBarLayout\n            android:id=\"@+id/profile_appbar\"\n            style=\"@style/ProfileAppBar\"\n            android:theme=\"@style/ThemeOverlay.AppCompat.Dark.ActionBar\">\n\n            <android.support.design.widget.CollapsingToolbarLayout\n                style=\"@style/ProfileCollapsingToolbar\">\n\n                <FrameLayout\n                    android:id=\"@+id/profile_user_container_layout\"\n                    style=\"@style/ProfileUserFrameLayout\">\n\n                    <LinearLayout\n                        android:id=\"@+id/profile_user_container\"\n                        style=\"@style/ProfileUserLinearLayout\">\n\n                        <ImageView\n                            android:id=\"@+id/profile_user_avatar\"\n                            style=\"@style/ProfileUserAvatar\"/>\n\n                        <TextView\n                            android:id=\"@+id/profile_user_name\"\n                            style=\"@style/ProfileUserName\"/>\n\n                        <TextView\n                            android:id=\"@+id/profile_user_email\"\n                            style=\"@style/ProfileUserEmail\"/>\n\n                    </LinearLayout>\n                </FrameLayout>\n            </android.support.design.widget.CollapsingToolbarLayout>\n\n            <android.support.design.widget.TabLayout\n                android:id=\"@+id/profile_tabs\"\n                style=\"@style/ProfileTabLayout\"/>\n\n        </android.support.design.widget.AppBarLayout>\n\n        <android.support.v7.widget.RecyclerView\n            android:id=\"@+id/profile_recycler\"\n            style=\"@style/ProfileRecyclerView\"/>\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/profile_toolbar\"\n            style=\"@style/ProfileToolbar\"\n            android:theme=\"@style/ThemeOverlay.AppCompat.Dark.ActionBar\"\n            app:layout_anchor=\"@id/profile_user_container_layout\"\n            app:popupTheme=\"@style/ThemeOverlay.AppCompat.Light\"/>\n\n    </android.support.design.widget.CoordinatorLayout>\n\n    <ProgressBar\n        android:id=\"@+id/profile_loading\"\n        style=\"@style/ProfileLoading\"/>\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/profile_subscription_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:id=\"@+id/subscriptions_item_layout\"\n        style=\"@style/SubscriptionsItemContent\">\n\n    <LinearLayout\n            style=\"@style/SubscriptionsItem\">\n\n        <FrameLayout\n                android:id=\"@+id/subscriptions_item_content\"\n                style=\"@style/SubscriptionsCollectionCheckBoxContent\">\n\n            <cards.nine.app.ui.components.widgets.CollectionCheckBox\n                    android:id=\"@+id/collection_subscription_checkbox\"\n                    style=\"@style/SubscriptionsCollectionCheckBox\" />\n\n        </FrameLayout>\n\n        <LinearLayout\n                style=\"@style/SubscriptionsItemNames\">\n\n            <TextView\n                    android:id=\"@+id/subscriptions_item_name\"\n                    style=\"@style/SubscriptionsItemName\"/>\n\n            <TextView\n                    android:id=\"@+id/subscriptions_item_status\"\n                    style=\"@style/SubscriptionsItemStatus\"/>\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n</LinearLayout>\n"
  },
  {
    "path": "modules/app/src/main/res/layout/public_collections_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<android.support.v7.widget.CardView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/public_collections_item_layout\"\n    style=\"@style/PublicCollectionsItemRoot\">\n\n    <LinearLayout\n        style=\"@style/PublicCollectionsItemContent\">\n\n        <LinearLayout\n            style=\"@style/PublicCollectionsItemHeader\">\n\n            <FrameLayout\n                style=\"@style/PublicCollectionsItemHeaderIconContent\"\n                android:id=\"@+id/public_collections_item_content\">\n\n                <ImageView\n                    android:id=\"@+id/public_collections_item_icon\"\n                    style=\"@style/PublicCollectionsItemHeaderIcon\"/>\n\n            </FrameLayout>\n\n            <LinearLayout\n                style=\"@style/PublicCollectionsItemHeaderNames\">\n\n                <TextView\n                    android:id=\"@+id/public_collections_item_name\"\n                    style=\"@style/PublicCollectionsItemHeaderName\"/>\n\n                <TextView\n                    android:id=\"@+id/public_collections_item_author\"\n                    style=\"@style/PublicCollectionsItemHeaderAuthor\"/>\n\n            </LinearLayout>\n\n            <TextView\n                android:id=\"@+id/public_collections_item_downloads\"\n                style=\"@style/PublicCollectionsItemHeaderDownloads\"/>\n\n        </LinearLayout>\n\n        <com.google.android.flexbox.FlexboxLayout\n            android:id=\"@+id/public_collections_item_apps\"\n            style=\"@style/PublicCollectionsItemIconsApps\"/>\n\n        <TextView\n            android:id=\"@+id/public_collections_item_subscriptions\"\n            style=\"@style/PublicCollectionsItemHeaderSubscriptions\"/>\n\n        <ImageView\n            android:id=\"@+id/public_collections_item_line\"\n            style=\"@style/PublicCollectionsItemLine\"/>\n\n        <LinearLayout\n            style=\"@style/PublicCollectionsItemActionsContent\">\n\n            <Button\n                android:id=\"@+id/public_collections_item_add_collection\"\n                style=\"@style/PublicCollectionsItemAddCollection\"/>\n\n            <ImageButton\n                android:id=\"@+id/public_collections_item_share_collection\"\n                style=\"@style/PublicCollectionsItemShareCollection\"/>\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n</android.support.v7.widget.CardView>\n"
  },
  {
    "path": "modules/app/src/main/res/layout/publish_collection_wizard.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/publish_collection_wizard_root\"\n    style=\"@style/PublishCollectionWizardRoot\">\n\n    <LinearLayout\n            android:id=\"@+id/publish_collection_wizard_start\"\n            style=\"@style/PublishCollectionWizardDialog\">\n\n        <LinearLayout\n                android:id=\"@+id/publish_collection_wizard_start_content\"\n                style=\"@style/PublishCollectionWizardContent\">\n\n            <TextView\n                    android:id=\"@+id/publish_collection_wizard_start_header\"\n                    style=\"@style/PublishCollectionWizardStartHeader\"/>\n\n            <TextView\n                    android:id=\"@+id/publish_collection_wizard_start_message\"\n                    style=\"@style/PublishCollectionWizardStartMessage\"/>\n\n            <ImageView\n                    android:id=\"@+id/publish_collection_wizard_start_image\"\n                    style=\"@style/PublishCollectionWizardImageStart\"/>\n\n        </LinearLayout>\n\n        <LinearLayout\n                android:id=\"@+id/publish_collection_wizard_start_footer\"\n                style=\"@style/PublishCollectionWizardStartFooter\">\n\n            <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/publish_collection_wizard_arrow\"\n                    style=\"@style/PublishCollectionWizardStartArrow\"/>\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <LinearLayout\n            android:id=\"@+id/publish_collection_wizard_information\"\n            style=\"@style/PublishCollectionWizardDialog\">\n\n        <LinearLayout\n                android:id=\"@+id/publish_collection_wizard_information_content\"\n                style=\"@style/PublishCollectionWizardContent\">\n\n            <TextView\n                    android:id=\"@+id/publish_collection_wizard_information_header\"\n                    style=\"@style/PublishCollectionWizardInformationHeader\"/>\n\n            <ScrollView style=\"@style/PublishCollectionWizardInformationScroll\">\n\n                <LinearLayout style=\"@style/PublishCollectionWizardInformationScrollContent\">\n\n                    <TextView\n                            android:id=\"@+id/publish_collection_information_message\"\n                            style=\"@style/PublishCollectionWizardInformationMessage\"/>\n\n                    <LinearLayout\n                            android:id=\"@+id/publish_collection_wizard_information_form\"\n                            style=\"@style/PublishCollectionWizardInformationForm\">\n\n                        <LinearLayout\n                                style=\"@style/PublishCollectionWizardInformationCollectionNameContent\">\n\n                            <TextView\n                                    android:id=\"@+id/collection_name_tag\"\n                                    style=\"@style/PublishCollectionWizardInformationCollectionNameTag\"/>\n\n                            <EditText\n                                    android:id=\"@+id/collection_name\"\n                                    style=\"@style/PublishCollectionWizardInformationCollectionNameInputMessage\"/>\n\n                            <cards.nine.app.ui.components.widgets.TintableImageView\n                                    android:id=\"@+id/collection_name_line\"\n                                    style=\"@style/PublishCollectionWizardInformationLineHorizontal\"/>\n\n                        </LinearLayout>\n\n                        <LinearLayout\n                                style=\"@style/PublishCollectionWizardInformationCategorySelectContent\">\n\n                            <TextView\n                                    android:id=\"@+id/category_tag\"\n                                    style=\"@style/PublishCollectionWizardInformationCategoryTag\"/>\n\n                            <LinearLayout\n                                    android:id=\"@+id/category_select\"\n                                    style=\"@style/PublishCollectionWizardInformationCategorySelect\">\n\n                                <TextView\n                                        android:id=\"@+id/category\"\n                                        style=\"@style/PublishCollectionWizardInformationCategorySpinner\"/>\n\n                                <cards.nine.app.ui.components.widgets.TintableImageView\n                                        android:id=\"@+id/category_indicator\"\n                                        style=\"@style/PublishCollectionWizardInformationCategoryIndicator\"/>\n\n                            </LinearLayout>\n\n                            <cards.nine.app.ui.components.widgets.TintableImageView\n                                    android:id=\"@+id/category_line\"\n                                    style=\"@style/PublishCollectionWizardInformationLineHorizontal\"/>\n\n                        </LinearLayout>\n\n                    </LinearLayout>\n\n                </LinearLayout>\n\n            </ScrollView>\n\n        </LinearLayout>\n\n        <LinearLayout\n                android:id=\"@+id/publish_collection_wizard_information_footer\"\n                style=\"@style/PublishCollectionWizardInformationFooter\">\n\n            <Button\n                    android:id=\"@+id/publish_collection_wizard_information_button\"\n                    style=\"@style/PublishCollectionWizardInformationButton\"/>\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <LinearLayout\n            android:id=\"@+id/publish_collection_wizard_publishing\"\n            style=\"@style/PublishCollectionWizardDialog\">\n\n        <LinearLayout\n                android:id=\"@+id/publish_collection_wizard_publishing_content\"\n                style=\"@style/PublishCollectionWizardContent\">\n\n            <TextView\n                    android:id=\"@+id/publish_collection_wizard_publishing_header\"\n                    style=\"@style/PublishCollectionWizardPublishingHeader\"/>\n\n            <TextView\n                    android:id=\"@+id/publish_collection_wizard_publishing_message\"\n                    style=\"@style/PublishCollectionWizardPublishingMessage\"/>\n\n            <LinearLayout\n                    android:id=\"@+id/publish_collection_wizard_loading\"\n                    style=\"@style/PublishCollectionWizardLoadingContent\">\n\n                <ProgressBar\n                        android:id=\"@+id/publish_collection_loading\"\n                        style=\"@style/PublishingLoading\"/>\n\n            </LinearLayout>\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <LinearLayout\n            android:id=\"@+id/publish_collection_wizard_end\"\n            style=\"@style/PublishCollectionWizardDialog\">\n\n        <LinearLayout\n                android:id=\"@+id/publish_collection_wizard_end_content\"\n                style=\"@style/PublishCollectionWizardContent\">\n\n            <TextView\n                    android:id=\"@+id/publish_collection_wizard_end_header\"\n                    style=\"@style/PublishCollectionWizardEndHeader\"/>\n\n            <TextView\n                    android:id=\"@+id/publish_collection_wizard_end_message\"\n                    style=\"@style/PublishCollectionWizardEndMessage\"/>\n\n            <ImageView\n                    android:id=\"@+id/publish_collection_wizard_end_image\"\n                    style=\"@style/PublishCollectionWizardImageEnd\"/>\n\n        </LinearLayout>\n\n        <LinearLayout\n                android:id=\"@+id/publish_collection_wizard_end_footer\"\n                style=\"@style/PublishCollectionWizardEndFooter\">\n\n            <cards.nine.app.ui.components.widgets.TintableImageView\n                    android:id=\"@+id/end_line\"\n                    style=\"@style/PublishCollectionWizardEndLineHorizontal\"/>\n\n            <Button\n                    android:id=\"@+id/publish_collection_wizard_end_button\"\n                    style=\"@style/PublishCollectionWizardEndButton\"/>\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <LinearLayout\n            android:id=\"@+id/publish_collection_wizard_steps_pagination_panel\"\n            style=\"@style/PublishCollectionWizardStepsPaginationPanel\"/>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/recommendations_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<android.support.v7.widget.CardView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/recommendation_item_layout\"\n    style=\"@style/RecommendationsItemRoot\">\n\n    <LinearLayout\n        style=\"@style/RecommendationsItemContent\">\n\n        <LinearLayout\n            style=\"@style/RecommendationsItemHeader\">\n\n            <ImageView\n                android:id=\"@+id/recommendation_item_icon\"\n                style=\"@style/RecommendationsItemHeaderIcon\"/>\n\n            <LinearLayout\n                style=\"@style/RecommendationsItemHeaderTitles\">\n\n                <TextView\n                    android:id=\"@+id/recommendation_item_name\"\n                    style=\"@style/RecommendationsItemHeaderName\"/>\n\n                <LinearLayout\n                    style=\"@style/RecommendationsItemHeaderTitleDataContent\">\n\n                    <ImageView\n                        android:id=\"@+id/recommendation_item_stars\"\n                        style=\"@style/RecommendationsItemHeaderStars\"/>\n\n                    <TextView\n                        android:id=\"@+id/recommendation_item_downloads\"\n                        style=\"@style/RecommendationsItemHeaderDownloads\"/>\n\n                </LinearLayout>\n\n            </LinearLayout>\n\n            <TextView\n                android:id=\"@+id/recommendation_item_tag\"\n                style=\"@style/RecommendationsItemHeaderTag\"/>\n\n        </LinearLayout>\n\n        <LinearLayout\n            style=\"@style/RecommendationsItemScreenshots\">\n\n            <ImageView\n                android:id=\"@+id/recommendation_item_screenshot1\"\n                style=\"@style/RecommendationsItemScreenshotItem\"/>\n\n            <ImageView\n                android:id=\"@+id/recommendation_item_screenshot2\"\n                style=\"@style/RecommendationsItemScreenshotItem\"/>\n\n            <ImageView\n                android:id=\"@+id/recommendation_item_screenshot3\"\n                style=\"@style/RecommendationsItemScreenshotItem\"/>\n\n        </LinearLayout>\n\n        <ImageView\n            android:id=\"@+id/recommendation_item_line\"\n            style=\"@style/RecommendationsItemLine\"/>\n\n        <Button\n            android:id=\"@+id/recommendation_item_install_now\"\n            style=\"@style/RecommendationsItemInstallNow\"/>\n\n    </LinearLayout>\n\n</android.support.v7.widget.CardView>\n"
  },
  {
    "path": "modules/app/src/main/res/layout/search_box_panel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/LauncherSearchDrawerPanel\"\n    android:id=\"@+id/launcher_search_box_panel\">\n\n    <ImageView\n        android:id=\"@+id/launcher_header_icon\"\n        style=\"@style/LauncherSearchHeader\" />\n\n    <ImageView style=\"@style/LauncherSearchHeaderLine\"/>\n\n    <EditText\n        style=\"@style/LauncherSearchEditText\"\n        android:id=\"@+id/launcher_search_box_text\"/>\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        style=\"@style/LauncherSearchIcon\"\n        android:id=\"@+id/launcher_search_box_action\"/>\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        style=\"@style/LauncherSearchIcon\"\n        android:id=\"@+id/launcher_search_box_icon\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/search_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/SearchItemRoot\">\n    \n    <ImageView\n        android:id=\"@+id/search_item_icon\"\n        style=\"@style/SearchItemIcon\"/>\n\n    <LinearLayout\n        style=\"@style/SearchItemInfoContent\">\n\n        <TextView\n            android:id=\"@+id/search_item_name\"\n            style=\"@style/SearchItemName\"/>\n\n        <LinearLayout\n            style=\"@style/SearchItemBottomInfoContent\">\n\n            <cards.nine.app.ui.components.widgets.TintableImageView\n                android:id=\"@+id/search_item_stars\"\n                style=\"@style/SearchItemStars\"/>\n\n            <TextView\n                android:id=\"@+id/search_item_downloads\"\n                style=\"@style/SearchItemDownloads\"/>\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <TextView\n        android:id=\"@+id/search_item_price\"\n        style=\"@style/SearchItemPrice\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/select_collection_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.v4.widget.NestedScrollView\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        style=\"@style/SelectCollectionDialogRoot\">\n\n    <LinearLayout\n            android:id=\"@+id/select_collection_list\"\n            style=\"@style/SelectCollectionDialogContent\"/>\n\n</android.support.v4.widget.NestedScrollView>\n"
  },
  {
    "path": "modules/app/src/main/res/layout/select_collection_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/SelectCollectionItemContent\">\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        android:id=\"@+id/select_collection_item_icon\"\n        style=\"@style/SelectCollectionItemIcon\"/>\n\n    <TextView\n        android:id=\"@+id/select_collection_item_text\"\n        style=\"@style/SelectCollectionItemText\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/select_moment_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.v4.widget.NestedScrollView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/SelectMomentDialogRoot\">\n\n    <LinearLayout\n            android:id=\"@+id/select_moment_list\"\n            style=\"@style/SelectMomentDialogContent\"/>\n\n</android.support.v4.widget.NestedScrollView>\n"
  },
  {
    "path": "modules/app/src/main/res/layout/select_moment_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/SelectMomentItemRoot\">\n\n    <LinearLayout\n        style=\"@style/SelectMomentItemContent\">\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            android:id=\"@+id/select_moment_item_icon\"\n            style=\"@style/SelectMomentItemIcon\"/>\n\n        <TextView\n            android:id=\"@+id/select_moment_item_text\"\n            style=\"@style/SelectMomentItemText\"/>\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            android:id=\"@+id/select_moment_item_pin\"\n            style=\"@style/SelectMomentItemPin\"/>\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            android:id=\"@+id/select_moment_item_edit\"\n            style=\"@style/SelectMomentItemEdit\"/>\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            android:id=\"@+id/select_moment_item_delete\"\n            style=\"@style/SelectMomentItemDelete\"/>\n\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/select_moment_item_line\"\n        style=\"@style/SelectMomentItemLine\" />\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/shortcut_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/ShortcutItemRoot\">\n    \n    <ImageView\n        android:id=\"@+id/simple_item_icon\"\n        style=\"@style/ShortcutItemIcon\"/>\n\n    <TextView\n        android:id=\"@+id/simple_item_name\"\n        style=\"@style/ShortcutItemName\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/swipe_animation_drawer_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/swipe_animation_root\"\n    style=\"@style/LauncherDrawerAnimatedContent\">\n\n    <ImageView\n        android:id=\"@+id/swipe_animation_content\"\n        style=\"@style/LauncherDrawerAnimatedRipple\"/>\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        android:id=\"@+id/swipe_animation_icon\"\n        style=\"@style/LauncherDrawerAnimatedIcon\"/>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/tab_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/TabItemRoot\">\n\n    <LinearLayout\n        style=\"@style/TabItemContent\">\n\n        <cards.nine.app.ui.components.widgets.TintableImageView\n            android:id=\"@+id/tab_item_icon\"\n            style=\"@style/TabItemIcon\"/>\n\n        <TextView\n            android:id=\"@+id/tab_item_name\"\n            style=\"@style/TabItemName\"/>\n\n    </LinearLayout>\n\n    <ImageView style=\"@style/TabItemLine\"/>\n\n    <ImageView\n        android:id=\"@+id/tab_item_line\"\n        style=\"@style/TabItemSelectLine\"/>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/toolbar_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    style=\"@style/BaseActionsToolbarRoot\">\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/actions_toolbar_widget\"\n        android:theme=\"@style/ThemeOverlay.AppCompat.Dark.ActionBar\"\n        app:popupTheme=\"@style/ThemeOverlay.AppCompat.Light\"\n        style=\"@style/BaseActionsToolbar\">\n\n        <TextView\n            android:id=\"@+id/actions_toolbar_title\"\n            style=\"@style/BaseActionsToolbarTitle\"/>\n\n        <EditText\n            android:id=\"@+id/actions_toolbar_search\"\n            style=\"@style/BaseActionsToolbarSearch\"/>\n\n    </android.support.v7.widget.Toolbar>\n\n    <FrameLayout\n        android:id=\"@+id/actions_toolbar_extended_content\"\n        style=\"@style/BaseActionsToolbarExtendedContent\" />\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/widget_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<android.support.v7.widget.CardView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/WidgetItemRoot\">\n\n    <LinearLayout\n        style=\"@style/WidgetItemContent\">\n\n        <LinearLayout\n            style=\"@style/WidgetItemHeaderContent\">\n\n            <TextView\n                android:id=\"@+id/widget_item_title\"\n                style=\"@style/WidgetItemTitle\"/>\n\n            <TextView\n                android:id=\"@+id/widget_item_cells\"\n                style=\"@style/WidgetItemCells\"/>\n\n        </LinearLayout>\n\n        <ImageView\n            android:id=\"@+id/widget_item_preview\"\n            style=\"@style/WidgetItemPreview\"/>\n\n    </LinearLayout>\n\n</android.support.v7.widget.CardView>\n"
  },
  {
    "path": "modules/app/src/main/res/layout/widgets_action_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/widgets_actions_panel\"\n    style=\"@style/WidgetsActionsListContent\">\n\n    <HorizontalScrollView\n        style=\"@style/WidgetsActionsScrollMenu\">\n\n        <LinearLayout\n            android:id=\"@+id/widgets_actions_menu\"\n            style=\"@style/WidgetsActionsMenu\" />\n\n    </HorizontalScrollView>\n\n    <android.support.v7.widget.RecyclerView\n        android:id=\"@+id/widgets_actions_recycler\"\n        style=\"@style/WidgetsActionsRecycler\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             android:id=\"@+id/wizard_root\"\n             style=\"@style/WizardRoot\">\n\n    <LinearLayout\n        android:id=\"@+id/wizard_loading_content\"\n        style=\"@style/WizardLoadingContent\">\n\n        <TextView\n            android:id=\"@+id/wizard_loading_text\"\n            style=\"@style/WizardLoadingMessage\"/>\n\n        <ProgressBar\n            android:id=\"@+id/wizard_loading_bar\"\n            style=\"@style/WizardLoadingBar\"/>\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:id=\"@+id/wizard_user_content\"\n        style=\"@style/WizardUserContent\">\n\n        <ImageView\n            android:id=\"@+id/wizard_user_logo\"\n            style=\"@style/WizardUserLogo\"/>\n\n        <TextView\n            android:id=\"@+id/wizard_user_title\"\n            style=\"@style/WizardUserTitle\"/>\n\n        <android.support.v7.widget.AppCompatButton\n            android:id=\"@+id/wizard_user_action\"\n            style=\"@style/WizardUserButton\"/>\n\n        <TextView\n            android:id=\"@+id/wizard_user_terms\"\n            style=\"@style/WizardUserTerms\"/>\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:id=\"@+id/wizard_device_content\"\n        style=\"@style/WizardDeviceContent\">\n\n        <TextView\n            android:id=\"@+id/wizard_device_title\"\n            style=\"@style/WizardDeviceTitle\"/>\n\n        <TextView\n            android:id=\"@+id/wizard_device_message\"\n            style=\"@style/WizardDeviceMessage\"/>\n\n        <ScrollView\n            style=\"@style/WizardDeviceRadioGroupScroll\">\n\n            <RadioGroup\n                android:id=\"@+id/wizard_device_group\"\n                style=\"@style/WizardDeviceRadioGroup\"/>\n\n        </ScrollView>\n\n        <android.support.v7.widget.AppCompatButton\n            android:id=\"@+id/wizard_device_action\"\n            style=\"@style/WizardDeviceButton\"/>\n\n    </LinearLayout>\n\n    <FrameLayout\n        android:id=\"@+id/wizard_steps_content\"\n        style=\"@style/WizardStepsContent\">\n\n        <ImageView\n            android:id=\"@+id/wizard_steps_background\"\n            style=\"@style/WizardBackgroundContent\"/>\n\n        <cards.nine.app.ui.components.layouts.StepsWorkspaces\n            android:id=\"@+id/wizard_steps_workspace\"\n            style=\"@style/WizardStepsWorkspace\" />\n\n        <Button\n            android:id=\"@+id/wizard_steps_action\"\n            style=\"@style/WizardStepsButton\"/>\n\n        <TextView\n            android:id=\"@+id/wizard_steps_downloading_message\"\n            style=\"@style/WizardStepsDownloadingMessage\"/>\n\n        <LinearLayout\n            style=\"@style/WizardStepsPaginationPanel\"\n            android:id=\"@+id/wizard_steps_pagination_panel\"/>\n\n    </FrameLayout>\n\n    <LinearLayout\n        android:id=\"@+id/wizard_steps_new_configuration_content\"\n        style=\"@style/WizardNewConfigurationRoot\">\n\n        <FrameLayout\n            android:id=\"@+id/wizard_steps_new_configuration_step\"\n            style=\"@style/WizardNewConfigurationStepContentRoot\"/>\n\n        <FrameLayout\n            style=\"@style/WizardNewConfigurationPaginationContent\">\n\n            <TextView\n                android:id=\"@+id/wizard_steps_new_configuration_pager\"\n                style=\"@style/WizardNewConfigurationPagers\" />\n\n            <LinearLayout\n                android:id=\"@+id/wizard_steps_new_configuration_next\"\n                style=\"@style/WizardNewConfigurationNext\">\n\n                <TextView\n                    android:id=\"@+id/wizard_steps_new_configuration_next_text\"\n                    style=\"@style/WizardNewConfigurationNextText\"/>\n\n                <ImageView\n                    android:id=\"@+id/wizard_steps_new_configuration_next_icon\"\n                    style=\"@style/WizardNewConfigurationNextIcon\"/>\n\n            </LinearLayout>\n\n        </FrameLayout>\n\n    </LinearLayout>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_checkbox.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/WizardCheckboxRoot\">\n\n    <ImageView\n        android:id=\"@+id/wizard_check_icon\"\n        style=\"@style/WizardCheckboxIcon\"/>\n\n    <TextView\n        android:id=\"@+id/wizard_check_text\"\n        style=\"@style/WizardCheckboxText\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_inline.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/WizardInlineContent\">\n\n    <cards.nine.app.ui.components.layouts.WizardInlineWorkspaces\n        android:id=\"@+id/wizard_inline_workspace\"\n        style=\"@style/WizardInlineWorkspace\"/>\n\n    <FrameLayout\n        style=\"@style/WizardInlineBottomPanel\">\n\n        <LinearLayout\n            style=\"@style/WizardInlinePaginationPanel\"\n            android:id=\"@+id/wizard_inline_pagination_panel\"/>\n\n        <TextView\n            style=\"@style/WizardInlineSkipAction\"\n            android:id=\"@+id/wizard_inline_skip\"/>\n\n        <TextView\n            style=\"@style/WizardInlineGotItAction\"\n            android:id=\"@+id/wizard_inline_got_it\"/>\n\n    </FrameLayout>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_inline_step.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/WizardInlineItemRoot\">\n\n    <TextView\n        android:id=\"@+id/wizard_inline_item_title\"\n        style=\"@style/WizardInlineItemTitle\"/>\n\n    <TextView\n        android:id=\"@+id/wizard_inline_item_message\"\n        style=\"@style/WizardInlineItemMessage\"/>\n\n    <ImageView\n        android:id=\"@+id/wizard_inline_item_image\"\n        style=\"@style/WizardInlineItemImage\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_moment_checkbox.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/WizardMomentCheckboxRoot\">\n\n    <FrameLayout\n        android:id=\"@+id/wizard_moment_check_content\"\n        style=\"@style/WizardMomentCheckboxIconContent\">\n\n        <ImageView\n            android:id=\"@+id/wizard_moment_check_icon\"\n            style=\"@style/WizardMomentCheckboxIcon\"/>\n\n        <FrameLayout\n            android:id=\"@+id/wizard_moment_check_tag_content\"\n            style=\"@style/WizardMomentCheckboxTagContent\">\n\n            <ImageView\n                android:id=\"@+id/wizard_moment_check_tag\"\n                style=\"@style/WizardMomentCheckboxTag\"/>\n\n        </FrameLayout>\n\n    </FrameLayout>\n\n    <TextView\n        android:id=\"@+id/wizard_moment_check_name\"\n        style=\"@style/WizardMomentCheckboxName\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_new_conf_step_0.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/WizardNewConfigurationStepContent\">\n\n    <FrameLayout\n        android:id=\"@+id/wizard_steps_new_configuration_step0_header_content\"\n        style=\"@style/WizardNewConfigurationHeaderContentStep0\">\n\n        <ImageView\n            android:id=\"@+id/wizard_steps_new_configuration_step0_header_image\"\n            style=\"@style/WizardNewConfigurationHeaderImageStep0\"/>\n\n    </FrameLayout>\n\n    <TextView\n        android:id=\"@+id/wizard_steps_new_configuration_step0_title\"\n        style=\"@style/WizardNewConfigurationTitleStep0\"/>\n\n    <ScrollView\n        style=\"@style/WizardNewConfigurationScrollStep0\">\n\n        <TextView\n            android:id=\"@+id/wizard_steps_new_configuration_step0_description\"\n            style=\"@style/WizardNewConfigurationDescriptionStep0\"/>\n\n    </ScrollView>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_new_conf_step_1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/WizardNewConfigurationStepContent\">\n\n    <TextView\n        android:id=\"@+id/wizard_steps_new_configuration_step1_title\"\n        style=\"@style/WizardNewConfigurationTitleStep1\"/>\n\n    <TextView\n        android:id=\"@+id/wizard_steps_new_configuration_step1_description\"\n        style=\"@style/WizardNewConfigurationDescriptionStep1\"/>\n\n    <ScrollView\n        style=\"@style/WizardNewConfigurationScrollStep1\">\n\n        <LinearLayout\n            android:id=\"@+id/wizard_steps_new_configuration_step1_collection_content\"\n            style=\"@style/WizardNewConfigurationScrollContentStep1\"/>\n\n    </ScrollView>\n\n    <ImageView\n        style=\"@style/WizardNewConfigurationHeaderLineStep1\"/>\n\n    <FrameLayout\n        style=\"@style/WizardNewConfigurationHeaderContentStep1\">\n\n        <cards.nine.app.ui.components.widgets.WizardCheckBox\n            android:id=\"@+id/wizard_steps_new_configuration_step1_all_collections\"\n            style=\"@style/WizardNewConfigurationHeaderCheckboxStep1\" />\n\n        <TextView\n            android:id=\"@+id/wizard_steps_new_configuration_step1_collection_count\"\n            style=\"@style/WizardNewConfigurationHeaderCollectionCountStep1\"/>\n\n    </FrameLayout>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_new_conf_step_2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/WizardNewConfigurationStepContent\">\n\n    <FrameLayout\n        android:id=\"@+id/wizard_steps_new_configuration_step2_header_content\"\n        style=\"@style/WizardNewConfigurationHeaderContentStep2\">\n\n        <ImageView\n            android:id=\"@+id/wizard_steps_new_configuration_step2_header_image1\"\n            style=\"@style/WizardNewConfigurationHeaderImageStep2_1\"/>\n\n        <ImageView\n            android:id=\"@+id/wizard_steps_new_configuration_step2_header_image2\"\n            style=\"@style/WizardNewConfigurationHeaderImageStep2_2\"/>\n\n    </FrameLayout>\n\n    <TextView\n        android:id=\"@+id/wizard_steps_new_configuration_step2_title\"\n        style=\"@style/WizardNewConfigurationTitleStep2\"/>\n\n    <ScrollView\n        style=\"@style/WizardNewConfigurationScrollStep2\">\n\n        <TextView\n            android:id=\"@+id/wizard_steps_new_configuration_step2_description\"\n            style=\"@style/WizardNewConfigurationDescriptionStep2\"/>\n\n    </ScrollView>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_new_conf_step_3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/WizardNewConfigurationStepContent\">\n\n    <TextView\n        style=\"@style/WizardNewConfigurationTitleStep3\"/>\n\n    <TextView\n        style=\"@style/WizardNewConfigurationDescriptionStep3\"/>\n\n    <ScrollView\n        style=\"@style/WizardNewConfigurationScrollStep3\">\n\n        <LinearLayout\n            android:id=\"@+id/wizard_steps_new_configuration_step3_wifi_content\"\n            style=\"@style/WizardNewConfigurationScrollContentStep3\"/>\n\n    </ScrollView>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_new_conf_step_4.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/WizardNewConfigurationStepContent\">\n\n    <TextView\n        style=\"@style/WizardNewConfigurationTitleStep4\"/>\n\n    <TextView\n        style=\"@style/WizardNewConfigurationDescriptionStep4\"/>\n\n    <LinearLayout\n        style=\"@style/WizardNewConfigurationContentMomentsStep4\">\n\n        <cards.nine.app.ui.components.widgets.WizardMomentCheckBox\n            android:id=\"@+id/wizard_moment_step4_music\"\n            style=\"@style/WizardNewConfigurationCheckStep4\" />\n\n        <cards.nine.app.ui.components.widgets.WizardMomentCheckBox\n            android:id=\"@+id/wizard_moment_step4_car\"\n            style=\"@style/WizardNewConfigurationCheckStep4\" />\n\n        <cards.nine.app.ui.components.widgets.WizardMomentCheckBox\n            android:id=\"@+id/wizard_moment_step4_sport\"\n            style=\"@style/WizardNewConfigurationCheckStep4\" />\n\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_new_conf_step_5.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/WizardNewConfigurationStepContent\">\n\n    <FrameLayout\n        android:id=\"@+id/wizard_steps_new_configuration_step5_header_content\"\n        style=\"@style/WizardNewConfigurationHeaderContentStep5\">\n\n        <ImageView\n            android:id=\"@+id/wizard_steps_new_configuration_step5_header_image\"\n            style=\"@style/WizardNewConfigurationHeaderImageStep5\"/>\n\n    </FrameLayout>\n\n    <TextView\n        android:id=\"@+id/wizard_steps_new_configuration_step5_title\"\n        style=\"@style/WizardNewConfigurationTitleStep5\"/>\n\n    <ScrollView\n        style=\"@style/WizardNewConfigurationScrollStep5\">\n\n        <TextView\n            android:id=\"@+id/wizard_steps_new_configuration_step5_description\"\n            style=\"@style/WizardNewConfigurationDescriptionStep5\"/>\n\n    </ScrollView>\n\n    <Button\n        android:id=\"@+id/wizard_moment_step5_go_to_9cards\"\n        style=\"@style/WizardNewConfigurationActionStep5\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_step.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/WizardStepItemRoot\">\n\n    <FrameLayout\n        style=\"@style/WizardStepItemImageContent\">\n\n        <ImageView\n            android:id=\"@+id/wizard_step_item_image\"\n            style=\"@style/WizardStepItemImage\"/>\n\n    </FrameLayout>\n\n    <TextView\n        android:id=\"@+id/wizard_step_item_title\"\n        style=\"@style/WizardStepItemTitle\"/>\n\n    <TextView\n        android:id=\"@+id/wizard_step_item_message\"\n        style=\"@style/WizardStepItemMessage\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/wizard_wifi_checkbox.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/WizardWifiCheckboxRoot\">\n\n    <ImageView\n        android:id=\"@+id/wizard_wifi_check_icon\"\n        style=\"@style/WizardWifiCheckboxIcon\"/>\n\n    <LinearLayout\n        style=\"@style/WizardWifiCheckboxNamesRoot\">\n\n        <TextView\n            android:id=\"@+id/wizard_wifi_check_name\"\n            style=\"@style/WizardWifiCheckboxName\"/>\n\n        <TextView\n            android:id=\"@+id/wizard_wifi_check_connected\"\n            style=\"@style/WizardWifiCheckboxConnectedText\"/>\n\n    </LinearLayout>\n\n    <ImageView\n        style=\"@style/WizardWifiCheckboxLine\"/>\n\n    <cards.nine.app.ui.components.widgets.TintableImageView\n        android:id=\"@+id/wizard_wifi_check_wifi_action\"\n        style=\"@style/WizardWifiCheckboxSelectWifi\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/workspace_button.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              style=\"@style/WorkSpaceMomentIcon\">\n\n    <FrameLayout\n            android:id=\"@+id/workspace_moment_icon_content\"\n            style=\"@style/WorkSpaceMomentIconDrawableContent\">\n\n        <ImageView\n                android:id=\"@+id/workspace_moment_icon\"\n                style=\"@style/WorkSpaceMomentIconDrawable\"/>\n\n    </FrameLayout>\n\n    <TextView\n            android:id=\"@+id/workspace_moment_title\"\n            style=\"@style/WorkSpaceMomentIconTitle\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/workspace_item_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/WorkspaceMenuItem\">\n\n    <ImageView\n        android:id=\"@+id/workspace_icon\"\n        style=\"@style/WorkspaceMenuItemIcon\"/>\n\n    <TextView\n        android:id=\"@+id/workspace_title\"\n        style=\"@style/WorkspaceMenuItemTitle\"/>\n\n</LinearLayout>"
  },
  {
    "path": "modules/app/src/main/res/layout/workspace_menu_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:id=\"@+id/menu_collection_root\"\n        style=\"@style/LauncherCollectionMenuRoot\">\n\n    <LinearLayout\n            android:id=\"@+id/menu_workspace_content\"\n            style=\"@style/LauncherCollectionMenuContent\" />\n\n\n    <LinearLayout\n            android:id=\"@+id/menu_launcher_content\"\n            style=\"@style/LauncherCollectionMenuContentBottom\">\n\n        <TextView\n                android:id=\"@+id/menu_launcher_wallpaper\"\n                style=\"@style/LauncherCollectionMenuButtonWallpaper\"/>\n\n        <TextView\n                android:id=\"@+id/menu_launcher_widgets\"\n                style=\"@style/LauncherCollectionMenuButtonWidgets\"/>\n\n        <TextView\n                android:id=\"@+id/menu_launcher_settings\"\n                style=\"@style/LauncherCollectionMenuButtonSetting\"/>\n\n    </LinearLayout>\n\n</FrameLayout>"
  },
  {
    "path": "modules/app/src/main/res/menu/app_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <group\n        android:id=\"@+id/menu_1\"\n        android:checkableBehavior=\"none\">\n        <item\n            android:id=\"@+id/menu_collections\"\n            android:icon=\"@drawable/menu_icon_collections\"\n            android:title=\"@string/collectionsTitle\"/>\n\n        <item\n            android:id=\"@+id/menu_moments\"\n            android:icon=\"@drawable/menu_icon_moments\"\n            android:title=\"@string/momentsTitle\"/>\n\n        <item\n            android:id=\"@+id/menu_profile\"\n            android:icon=\"@drawable/menu_icon_profile\"\n            android:title=\"@string/profileTitle\"/>\n    </group>\n\n    <group\n        android:id=\"@+id/menu_2\"\n        android:checkableBehavior=\"none\">\n\n        <item\n            android:id=\"@+id/menu_wallpaper\"\n            android:title=\"@string/wallpaperTitle\"\n            android:visible=\"true\"/>\n\n        <item\n            android:id=\"@+id/menu_widget\"\n            android:title=\"@string/widgetsTitle\"\n            android:visible=\"true\"/>\n\n        <item\n            android:id=\"@+id/menu_settings\"\n            android:title=\"@string/nineCardsSettingsTitle\"\n            android:visible=\"true\"/>\n\n    </group>\n\n</menu>"
  },
  {
    "path": "modules/app/src/main/res/menu/collection_detail_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/action_add_apps\"\n        android:title=\"@string/addApps\"\n        app:showAsAction=\"never\"/>\n\n    <item\n        android:id=\"@+id/action_add_recommendation\"\n        android:title=\"@string/addRecommendations\"\n        app:showAsAction=\"never\"/>\n\n    <item\n        android:id=\"@+id/action_add_contact\"\n        android:title=\"@string/addContact\"\n        app:showAsAction=\"never\"/>\n\n    <item\n        android:id=\"@+id/action_add_shortcut\"\n        android:title=\"@string/addShortcut\"\n        app:showAsAction=\"never\"/>\n\n    <item\n        android:id=\"@+id/action_make_public\"\n        android:title=\"@string/make_public\"\n        app:showAsAction=\"never\"/>\n\n    <item\n        android:id=\"@+id/action_share\"\n        android:title=\"@string/share\"\n        app:showAsAction=\"never\"/>\n\n</menu>"
  },
  {
    "path": "modules/app/src/main/res/menu/collection_edit_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/action_edit\"\n        android:title=\"@string/edit\"\n        android:icon=\"@drawable/icon_action_edit\"\n        app:showAsAction=\"ifRoom\"/>\n\n    <item\n        android:id=\"@+id/action_move_to_collection\"\n        android:title=\"@string/moveTo\"\n        android:icon=\"@drawable/icon_action_move\"\n        app:showAsAction=\"ifRoom\"/>\n\n    <item\n        android:id=\"@+id/action_delete\"\n        android:title=\"@string/delete\"\n        android:icon=\"@drawable/icon_action_delete\"\n        app:showAsAction=\"ifRoom\"/>\n\n</menu>"
  },
  {
    "path": "modules/app/src/main/res/menu/profile_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/action_logout\"\n        android:orderInCategory=\"100\"\n        app:showAsAction=\"never\"\n        android:title=\"@string/logout\" />\n\n</menu>"
  },
  {
    "path": "modules/app/src/main/res/values/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string-array name=\"themes\">\n        <item>@string/themesDark</item>\n        <item>@string/themesLight</item>\n    </string-array>\n\n    <string-array name=\"themesValues\">\n        <item>0</item>\n        <item>1</item>\n    </string-array>\n\n    <string-array name=\"googleLogos\">\n        <item>@string/themeColourGoogleLogo</item>\n        <item>@string/colouredGoogleLogo</item>\n    </string-array>\n\n    <string-array name=\"googleLogosValues\">\n        <item>0</item>\n        <item>1</item>\n    </string-array>\n\n    <string-array name=\"drawerActions\">\n        <item>@string/appDrawerOpenKeyboard</item>\n        <item>@string/appDrawerOpenContacts</item>\n    </string-array>\n\n    <string-array name=\"drawerActionsValues\">\n        <item>0</item>\n        <item>1</item>\n    </string-array>\n\n    <string-array name=\"appDrawerOpenAnimations\">\n        <item>@string/appDrawerOpenAnimationReveal</item>\n        <item>@string/appDrawerOpenAnimationFade</item>\n    </string-array>\n\n    <string-array name=\"appDrawerOpenAnimationsValues\">\n        <item>0</item>\n        <item>1</item>\n    </string-array>\n\n    <string-array name=\"fonts\">\n        <item>@string/fontsSmall</item>\n        <item>@string/fontsMedium</item>\n        <item>@string/fontsLarge</item>\n    </string-array>\n\n    <string-array name=\"fontsValues\">\n        <item>0</item>\n        <item>1</item>\n        <item>2</item>\n    </string-array>\n\n    <string-array name=\"icons\">\n        <item>@string/iconsSmall</item>\n        <item>@string/iconsMedium</item>\n        <item>@string/iconsLarge</item>\n    </string-array>\n\n    <string-array name=\"iconsValues\">\n        <item>0</item>\n        <item>1</item>\n        <item>2</item>\n    </string-array>\n\n    <string-array name=\"cardPadding\">\n        <item>@string/cardPaddingSmall</item>\n        <item>@string/cardPaddingMedium</item>\n        <item>@string/cardPaddingLarge</item>\n    </string-array>\n\n    <string-array name=\"cardPaddingValues\">\n        <item>0</item>\n        <item>1</item>\n        <item>2</item>\n    </string-array>\n\n    <string-array name=\"speeds\">\n        <item>@string/speedsSlow</item>\n        <item>@string/speedsNormal</item>\n        <item>@string/speedsFast</item>\n    </string-array>\n\n    <string-array name=\"speedsValues\">\n        <item>0</item>\n        <item>1</item>\n        <item>2</item>\n    </string-array>\n\n    <string-array name=\"collectionOpenings\">\n        <item>@string/collectionOpeningsCircleReveal</item>\n        <item>@string/collectionOpeningsNoAnimation</item>\n    </string-array>\n\n    <string-array name=\"collectionOpeningsValues\">\n        <item>0</item>\n        <item>1</item>\n    </string-array>\n\n    <string-array name=\"workspaceAnimations\">\n        <item>@string/workspaceAnimationsHorizontalSlide</item>\n        <item>@string/workspaceAnimationsAppearsBehind</item>\n    </string-array>\n\n    <string-array name=\"workspaceAnimationsValues\">\n        <item>0</item>\n        <item>1</item>\n    </string-array>\n\n    <string-array name=\"days_letters\">\n        <item>S</item>\n        <item>M</item>\n        <item>T</item>\n        <item>W</item>\n        <item>T</item>\n        <item>F</item>\n        <item>S</item>\n    </string-array>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Main colors -->\n    <color name=\"background_app\">#fefefe</color>\n    <color name=\"primary\">#3F51B5</color>\n    <color name=\"primary_dark\">#ff344285</color>\n    <color name=\"color_control_highlight\">#cccccc</color>\n    <color name=\"accent\">#3F51B5</color>\n    <color name=\"toolbar_title\">#ffffff</color>\n    <color name=\"toolbar_hint_title\">#a0ffffff</color>\n\n    <color name=\"background_default_1\">#FF9800</color>\n    <color name=\"background_default_2\">#F44336</color>\n    <color name=\"background_default_3\">#9C27B0</color>\n    <color name=\"background_default_4\">#2196F3</color>\n    <color name=\"background_default_5\">#673AB7</color>\n\n    <color name=\"background_dialog\">#AA000000</color>\n\n    <color name=\"shadow_default\">#32364550</color>\n\n    <color name=\"background_box_content\">#ffffff</color>\n\n    <color name=\"checkbox_selected\">#4caf4f</color>\n    <color name=\"checkbox_light_unselected\">#c0c0c0</color>\n    <color name=\"checkbox_dark_unselected\">#4d646e</color>\n\n    <!-- Launcher -->\n\n    <color name=\"text_launcher_action\">#ffffff</color>\n\n    <!-- Texts -->\n\n    <color name=\"text_default_title\">#db000000</color>\n    <color name=\"text_default_subtitle\">#8f000000</color>\n\n    <!-- Launcher -->\n\n    <color name=\"text_launcher_moment_panel\">#ffffff</color>\n\n    <color name=\"stroke_rules_moment\">#a0ffffff</color>\n\n    <color name=\"stroke_widget_selected\">#ffffff</color>\n\n    <!-- Wizard Inline -->\n    <color name=\"wizard_inline_background\">#3F51B5</color>\n    <color name=\"wizard_inline_title\">#FBFBFD</color>\n    <color name=\"wizard_inline_subtitle\">#bdfbfbfd</color>\n\n    <color name=\"wizard_inline_pager_current\">#ffffff</color>\n    <color name=\"wizard_inline_pager_default\">#95ffffff</color>\n\n    <!-- Wizard -->\n\n    <color name=\"wizard_text_button_on\">#ffffff</color>\n    <color name=\"wizard_text_button_off\">#77ffffff</color>\n    <color name=\"wizard_text_terms\">#88000000</color>\n\n    <color name=\"wizard_text_title\">#000000</color>\n    <color name=\"wizard_text_message\">#88000000</color>\n\n    <color name=\"wizard_text_step_item_message\">#292929</color>\n\n    <color name=\"wizard_text_downloading\">#848484</color>\n\n    <color name=\"wizard_background_action_disable\">#D9D9D9</color>\n    <color name=\"wizard_background_action_enable\">#BF3126</color>\n\n    <color name=\"wizard_pager_current\">#737373</color>\n    <color name=\"wizard_pager_default\">#D9D9D9</color>\n\n    <color name=\"wizard_background_step_1\">#3F51B5</color>\n    <color name=\"wizard_background_step_2\">#4CAF4F</color>\n    <color name=\"wizard_background_step_3\">#2196F3</color>\n    <color name=\"wizard_background_step_4\">#FF9800</color>\n    <color name=\"wizard_background_step_5\">#F44336</color>\n\n    <color name=\"wizard_new_conf_accent_1\">#4CAF4F</color>\n    <color name=\"wizard_new_conf_accent_2\">#3F51B5</color>\n    <color name=\"wizard_new_conf_accent_3\">#FF9800</color>\n    <color name=\"wizard_new_conf_accent_4\">#F44336</color>\n\n    <color name=\"wizard_checkbox_unselected\">#47000000</color>\n\n    <color name=\"wizard_line\">#437b7b7b</color>\n\n    <!-- App Drawer -->\n    <color name=\"drawer_pager_current\">#ffffff</color>\n    <color name=\"drawer_pager_default\">#6bffffff</color>\n\n    <color name=\"divider_color_search_box\">#1f000000</color>\n\n    <!-- Menu -->\n    <color name=\"background_menu_header\">#3F51B5</color>\n    <color name=\"text_menu_name\">#ffffff</color>\n    <color name=\"text_menu_email\">#b5ffffff</color>\n\n    <!-- WorkSpace -->\n    <color name=\"collection_workspace_button_item_title\">#ffffff</color>\n    <color name=\"collection_workspace_feedback_drop\">#77ffffff</color>\n\n    <color name=\"moment_workspace_background\">#22ffffff</color>\n\n    <!-- Groups of collections -->\n    <color name=\"collection_group_name\">#ffffff</color>\n    <color name=\"workspaces_pager_default\">#32ffffff</color>\n    <color name=\"workspaces_pager_selected\">#ffffff</color>\n\n    <color name=\"collection_group_1\">#FF9800</color>\n    <color name=\"collection_group_2\">#F44336</color>\n    <color name=\"collection_group_3\">#9C27B0</color>\n    <color name=\"collection_group_4\">#2196F3</color>\n    <color name=\"collection_group_5\">#673AB7</color>\n    <color name=\"collection_group_6\">#3F51B5</color>\n    <color name=\"collection_group_7\">#8BC34A</color>\n    <color name=\"collection_group_8\">#4CAF50</color>\n    <color name=\"collection_group_9\">#009688</color>\n\n    <color name=\"collection_fab_button_item_title\">#000000</color>\n    <color name=\"collection_fab_button_item_title_background_default\">#ffffff</color>\n    <color name=\"collection_fab_button_item_title_background_pressed\">#cccccc</color>\n\n    <color name=\"collection_fab_button_item_wallpaper\">#009688</color>\n    <color name=\"collection_fab_button_item_widgets\">#3F51B5</color>\n    <color name=\"collection_fab_button_item_settings\">#F44336</color>\n\n    <color name=\"collection_fab_button_item_1\">#ff9800</color>\n    <color name=\"collection_fab_button_item_2\">#3f51b5</color>\n    <color name=\"collection_fab_button_item_3\">#F44336</color>\n\n    <color name=\"collection_detail_fab_button_item\">#3F51B5</color>\n    <color name=\"collection_detail_fab_button_item_ripple\">#ff1a2346</color>\n\n    <!-- Widgets -->\n    <color name=\"widgets_background\">#263238</color>\n    <color name=\"widgets_background_content\">#212c32</color>\n    <color name=\"widgets_background_header\">#37474f</color>\n    <color name=\"widgets_text\">#ffffff</color>\n\n    <!-- Collections -->\n    <color name=\"hint_collection_name\">#898989</color>\n\n    <!-- Collections detail -->\n    <color name=\"background_icon_collection_detail\">#4bffffff</color>\n    <color name=\"toolbar_title_collection_detail\">#eeffffff</color>\n    <color name=\"text_tab_color_default\">#77ffffff</color>\n    <color name=\"text_tab_color_selected\">#ffffff</color>\n\n    <!-- Windows actions -->\n    <color name=\"background_actions\">#EEEEEE</color>\n\n    <!-- Fast Scroller -->\n    <color name=\"fastscroller_text\">#ffffff</color>\n\n    <!-- PullToTabsView -->\n    <color name=\"pulltotabs_line\">#24000000</color>\n\n    <!-- Drawer -->\n    <color name=\"drawer_status_bar\">#ff2F3E9E</color>\n    <color name=\"drawer_toolbar\">#ff3E50B4</color>\n    <color name=\"drawer_fab_button\">#ff303aa6</color>\n    <color name=\"drawer_fab_button_ripple\">#ff0e1550</color>\n    <color name=\"drawer_tab_bottom\">#ffffff</color>\n\n    <color name=\"drawer_search_box\">#88000000</color>\n    <color name=\"drawer_search_box_hint\">#4c000000</color>\n\n    <!-- Contact Info Dialog -->\n    <color name=\"divider_color_info_dialog_no_alpha\">#00d6d6d6</color>\n    <color name=\"color_selected_color_dialog\">#ffffff</color>\n    <color name=\"text_item_value\">#000000</color>\n    <color name=\"text_item_category\">#89000000</color>\n    <color name=\"background_header_content\">#29000000</color>\n    <color name=\"text_dialog_name\">#ffffff</color>\n\n    <!-- Publish Collection Dialog -->\n    <color name=\"footer_text_color\">#636363</color>\n    <color name=\"divider_category_color\">#626262</color>\n    <color name=\"publish_collection_wizard_pager_current\">#ffffff</color>\n\n    <!-- Public Collection Dialog -->\n    <color name=\"background_count_public_collection_dialog\">#cdcdcd</color>\n    <color name=\"tab_public_collection_dialog\">#ffffff</color>\n\n    <!-- ListPopupMenu -->\n    <color name=\"item_list_popup_menu\">#000000</color>\n\n    <!-- Error Dialog -->\n    <color name=\"error_dialog_text_button\">#ffffff</color>\n\n    <!-- Edit Moment -->\n\n    <color name=\"edit_moment_unselected_day\">#999999</color>\n\n    <!-- About -->\n\n    <color name=\"preference_about_title\">#222222</color>\n    <color name=\"preference_about_desc\">#777777</color>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Api -->\n    <string name=\"api_base_url\">${backend.v1.url}</string>\n    <string name=\"api_app_id\">${backend.v1.appid}</string>\n    <string name=\"api_app_key\">${backend.v1.appkey}</string>\n    <string name=\"api_localization\">en-EN</string>\n    <string name=\"api_v2_base_url\">${backend.v2.url}</string>\n    <string name=\"api_v2_client_id\">${backend.v2.clientid}</string>\n\n    <!-- Google -->\n    <string name=\"profile_and_drive_oauth_scopes\">oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/drive.appdata</string>\n    <string name=\"android_market_oauth_scopes\">androidmarket</string>\n\n    <!-- Google Play -->\n    <string name=\"google_play_url\">http://play.google.com/store/apps/details</string>\n\n    <!-- Google Analytics -->\n    <string name=\"analytics_enabled\">${analytics.enabled}</string>\n    <string name=\"ga_trackingId\">${analytics.trackid}</string>\n\n    <!-- 9Cards -->\n    <string name=\"icons_apps_folder\">icons_apps</string>\n    <string name=\"shared_preferences_key\">ninecards-shared-preferences</string>\n    <string name=\"user_id_key\">ninecards-user-id</string>\n    <string name=\"bluetooth_key\">bluetooth-devices</string>\n\n    <string name=\"shared_collection_url\">http://9c.io/shared-collection/%1$s</string>\n\n    <!-- Crashlytics -->\n    <string name=\"crashlytics_enabled\">${crashlytics.enabled}</string>\n    <string name=\"crashlytics_api_key\">${crashlytics.apikey}</string>\n\n    <!-- Debug options -->\n    <string name=\"strict_mode_enabled\">${strictmode.enabled}</string>\n\n    <!-- Preferences About -->\n    <string name=\"nine_cards_google_play\">https://play.google.com/store/apps/details?id=com.fortysevendeg.ninecardslauncher</string>\n    <string name=\"follow_us_twitter\">http://twitter.com/47deg</string>\n    <string name=\"follow_us_facebook\">https://www.facebook.com/47degreesllc</string>\n    <string name=\"follow_us_google_plus\">https://plus.google.com/+47deg</string>\n    <string name=\"open_source\">http://github.com/47deg</string>\n    <string name=\"web_47\">http://www.47deg.com</string>\n    <string name=\"web_tos\">http://9c.io/tos</string>\n    <string name=\"nine_cards_github\">https://github.com/47deg/nine-cards-v2/</string>\n\n    <!-- Preferences Help -->\n    <string name=\"ninecards_help\">http://www.9cards.io/</string>\n\n    <!-- Firebase -->\n    <string name=\"firebase_enabled\" translatable=\"false\">${firebase.enabled}</string>\n    <string name=\"firebase_database_url\" translatable=\"false\">${firebase.url}</string>\n    <string name=\"google_app_id\" translatable=\"false\">${firebase.google.appid}</string>\n    <string name=\"google_api_key\" translatable=\"false\">${firebase.google.apikey}</string>\n    <string name=\"gcm_defaultSenderId\" translatable=\"false\">${firebase.gcm.senderid}</string>\n    <string name=\"default_web_client_id\" translatable=\"false\">${firebase.clientid}</string>\n\n    <!-- FlowUp -->\n    <string name=\"flowup_enabled\" translatable=\"false\">${flowup.enabled}</string>\n    <string name=\"flowup_apikey\" translatable=\"false\">${flowup.apikey}</string>\n\n    <!-- Apptentive -->\n    <string name=\"apptentive_enabled\" translatable=\"false\">${apptentive.enabled}</string>\n    <string name=\"apptentive_apikey\" translatable=\"false\">${apptentive.apikey}</string>\n\n    <!-- Dimen -->\n    <string name=\"dimen_name\" translatable=\"false\">default</string>\n\n</resources>\n"
  },
  {
    "path": "modules/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Size by density -->\n\n    <dimen name=\"text_xxxxlarge\">24sp</dimen>\n    <dimen name=\"text_xxxlarge\">20sp</dimen>\n    <dimen name=\"text_xxlarge\">18sp</dimen>\n    <dimen name=\"text_xlarge\">16sp</dimen>\n    <dimen name=\"text_large\">14sp</dimen>\n    <dimen name=\"text_default\">12sp</dimen>\n    <dimen name=\"text_medium\">10sp</dimen>\n    <dimen name=\"text_small\">8sp</dimen>\n    <dimen name=\"text_micro\">7sp</dimen>\n\n    <dimen name=\"card_padding_small\">1dp</dimen>\n    <dimen name=\"card_padding_medium\">2dp</dimen>\n    <dimen name=\"card_padding_large\">4dp</dimen>\n\n    <dimen name=\"size_group_collection_small\">56dp</dimen>\n    <dimen name=\"size_group_collection_medium\">64dp</dimen>\n    <dimen name=\"size_group_collection_large\">72dp</dimen>\n\n    <dimen name=\"size_icon_app_small\">44dp</dimen>\n    <dimen name=\"size_icon_app_medium\">52dp</dimen>\n    <dimen name=\"size_icon_app_large\">60dp</dimen>\n\n    <dimen name=\"height_search_box\">42dp</dimen>\n\n    <dimen name=\"padding_small\">4dp</dimen>\n    <dimen name=\"padding_default\">8dp</dimen>\n    <dimen name=\"padding_medium\">10dp</dimen>\n    <dimen name=\"padding_large\">12dp</dimen>\n    <dimen name=\"padding_xlarge\">18dp</dimen>\n    <dimen name=\"padding_xxlarge\">24dp</dimen>\n    <dimen name=\"padding_xxxlarge\">32dp</dimen>\n    <dimen name=\"padding_xxxxlarge\">52dp</dimen>\n\n    <!-- Commons -->\n\n    <dimen name=\"padding_checkbox\">30dp</dimen>\n    <dimen name=\"padding_select_icon\">2dp</dimen>\n\n    <dimen name=\"size_icon_small\">36dp</dimen>\n    <dimen name=\"size_icon_default\">48dp</dimen>\n    <dimen name=\"size_icon_medium\">54dp</dimen>\n    <dimen name=\"size_icon_large\">64dp</dimen>\n\n    <dimen name=\"radius_default\">3dp</dimen>\n\n    <dimen name=\"divider_default\">1px</dimen>\n    <dimen name=\"divider_wide\">3px</dimen>\n\n    <dimen name=\"elevation_box_workspaces\">6dp</dimen>\n    <dimen name=\"elevation_toolbar\">4dp</dimen>\n    <dimen name=\"elevation_default\">2dp</dimen>\n    <dimen name=\"elevation_pressed\">4dp</dimen>\n    <dimen name=\"elevation_fab_button\">6dp</dimen>\n    <dimen name=\"elevation_fab_button_pressed\">12dp</dimen>\n\n    <dimen name=\"elevation_collection_default\">4dp</dimen>\n    <dimen name=\"elevation_collection_up\">6dp</dimen>\n\n    <dimen name=\"stroke_thin\">1dp</dimen>\n    <dimen name=\"stroke_default\">2dp</dimen>\n    <dimen name=\"stroke_large\">4dp</dimen>\n\n    <dimen name=\"shadow_displacement_default\">1dp</dimen>\n    <dimen name=\"shadow_radius_default\">1dp</dimen>\n\n    <integer name=\"anim_duration_fast\">170</integer>\n    <integer name=\"anim_duration_normal\">220</integer>\n    <integer name=\"anim_duration_slow\">270</integer>\n\n    <dimen name=\"footer_height_default\">48dp</dimen>\n\n    <!-- Wizard inline -->\n    <dimen name=\"height_wizard_inline_bottom_panel\">70dp</dimen>\n\n    <!-- Top bar -->\n    <dimen name=\"width_icon_content_top_bar_moment\">54dp</dimen>\n    <dimen name=\"height_icon_content_top_bar_moment\">48dp</dimen>\n    <dimen name=\"size_icon_top_bar_moment\">24dp</dimen>\n    <dimen name=\"radius_icon_top_bar_moment\">20dp</dimen>\n\n    <!-- Apps moment bar -->\n    <dimen name=\"size_apps_bar_moment\">86dp</dimen>\n    <dimen name=\"height_header_apps_bar_moment\">104dp</dimen>\n    <dimen name=\"size_icon_apps_bar_moment\">40dp</dimen>\n\n    <!-- WorkSpace -->\n    <dimen name=\"size_workspace_moment_icon\">60dp</dimen>\n    <dimen name=\"margin_top_workspace_moment_background\">24dp</dimen>\n    <dimen name=\"size_workspace_menu_item\">50dp</dimen>\n    <dimen name=\"max_width_launcher_menu_item\">100dp</dimen>\n    <dimen name=\"size_edge_between_workspaces\">15dp</dimen>\n\n    <dimen name=\"size_launcher_pager\">12dp</dimen>\n\n    <!-- FAB -->\n    <dimen name=\"margin_fab_button\">0dp</dimen>\n    <dimen name=\"margin_horizontal_fab_button_item\">23dp</dimen>\n    <dimen name=\"margin_vertical_fab_button_item\">12dp</dimen>\n    <dimen name=\"size_fab_menu_item\">40dp</dimen>\n\n    <!-- Wizard -->\n    <dimen name=\"wizard_min_width_button\">200dp</dimen>\n    <dimen name=\"wizard_max_width_step_title\">300dp</dimen>\n    <dimen name=\"wizard_max_width_step_message\">260dp</dimen>\n    <integer name=\"wizard_anim_ripple_duration\">400</integer>\n\n    <dimen name=\"wizard_margin_top_logo\">80dp</dimen>\n\n    <dimen name=\"wizard_margin_pager\">6dp</dimen>\n    <dimen name=\"wizard_size_pager\">10dp</dimen>\n\n    <dimen name=\"margin_left_subtitle\">40dp</dimen>\n\n    <dimen name=\"wizard_height_image_content\">300dp</dimen>\n    <dimen name=\"wizard_height_header_steps\">280dp</dimen>\n\n    <dimen name=\"wizard_size_checkbox_title\">24dp</dimen>\n    <dimen name=\"wizard_size_checkbox_collection\">38dp</dimen>\n\n    <dimen name=\"wizard_min_width_checkbox_wifi\">200dp</dimen>\n\n    <dimen name=\"wizard_size_moment_checkbox\">75dp</dimen>\n\n    <!-- Widgets -->\n    <dimen name=\"size_widget_item_preview\">150dp</dimen>\n    <dimen name=\"size_widget_icon\">40dp</dimen>\n\n    <dimen name=\"height_edit_widgets_bottom\">180dp</dimen>\n    <dimen name=\"size_widget_cursor\">40dp</dimen>\n\n    <!-- App Drawer -->\n    <dimen name=\"drawer_size_pager\">6dp</dimen>\n    <dimen name=\"margin_pager_drawer\">2dp</dimen>\n\n    <integer name=\"appdrawer_alpha_unselected_item_percentage\">40</integer>\n\n    <!-- Menu -->\n    <dimen name=\"menu_height\">170dp</dimen>\n    <dimen name=\"menu_size_avatar\">54dp</dimen>\n\n    <!-- Groups of collections -->\n    <dimen name=\"margin_pager_collection\">2dp</dimen>\n    <integer name=\"anim_duration_pager_appear\">175</integer>\n\n    <dimen name=\"size_edge_workspace\">6dp</dimen>\n\n    <dimen name=\"margin_bottom_fab_menu_content_collections\">60dp</dimen>\n\n    <!-- Collections Details -->\n    <dimen name=\"size_icon_collection_detail\">60dp</dimen>\n    <dimen name=\"pivot_x_icon_collection_detail\">30dp</dimen>\n    <dimen name=\"margin_top_pagers_collection_details\">78dp</dimen>\n    <dimen name=\"margin_top_tabs_collection_details\">86dp</dimen>\n    <dimen name=\"height_tabs_collection_details\">40dp</dimen>\n    <dimen name=\"height_toolbar_collection_details\">130dp</dimen>\n    <dimen name=\"space_moving_collection_details\">56dp</dimen>\n    <integer name=\"anim_duration_icon_collection_detail\">100</integer>\n\n    <dimen name=\"size_icon_title_collapse_collection_details\">22dp</dimen>\n    <dimen name=\"padding_selector_collection_details\">2dp</dimen>\n\n    <dimen name=\"space_enter_views_collection_detail\">128dp</dimen>\n\n    <dimen name=\"padding_icon_home_indicator\">8dp</dimen>\n\n    <integer name=\"anim_duration_appear_toolbar\">400</integer>\n\n    <dimen name=\"distance_to_valid_action\">32dp</dimen>\n\n    <dimen name=\"height_selected_tab\">2dp</dimen>\n    <dimen name=\"text_tabs\">14sp</dimen>\n\n    <dimen name=\"size_actions_drag_drop\">54dp</dimen>\n\n    <dimen name=\"margin_top_empty_collection\">60dp</dimen>\n    <dimen name=\"padding_empty_collection\">30dp</dimen>\n    <dimen name=\"padding_empty_collection_message\">30dp</dimen>\n    <dimen name=\"padding_empty_collection_image\">40dp</dimen>\n\n    <dimen name=\"size_selected_icon\">24dp</dimen>\n\n    <!-- App Drawer -->\n    <dimen name=\"size_icon_app_drawer\">52dp</dimen>\n    <dimen name=\"margin_app_drawer_fab_button\">16dp</dimen>\n    <dimen name=\"width_popup_app_drawer\">220dp</dimen>\n\n    <!-- Dialog Actions -->\n    <dimen name=\"min_height_actions_dialogs\">10000dp</dimen>\n    <dimen name=\"height_simple_category\">28dp</dimen>\n    <dimen name=\"height_app_item\">112dp</dimen>\n    <dimen name=\"height_contact_item\">64dp</dimen>\n    <dimen name=\"height_search_item\">68dp</dimen>\n\n    <dimen name=\"height_extended_toolbar_dialog\">104dp</dimen>\n    <dimen name=\"margin_top_extended_content_toolbar\">54dp</dimen>\n\n    <dimen name=\"header_height\">150dp</dimen>\n\n    <dimen name=\"size_icon_shortcut\">40dp</dimen>\n\n    <dimen name=\"size_icon_contact\">40dp</dimen>\n\n    <dimen name=\"size_icon_contact_small\">28dp</dimen>\n\n    <dimen name=\"size_icon_contact_dialog\">60dp</dimen>\n\n    <dimen name=\"size_icon_recommendation\">42dp</dimen>\n\n    <dimen name=\"height_screenshot\">180dp</dimen>\n\n    <dimen name=\"height_select_new_collection\">54dp</dimen>\n\n    <dimen name=\"size_icon_select_new_collection\">28dp</dimen>\n\n    <dimen name=\"size_icon_color_dialog\">60dp</dimen>\n\n    <dimen name=\"margin_top_fab_button\">8dp</dimen>\n\n    <dimen name=\"size_icon_item_collections_content\">40dp</dimen>\n\n    <dimen name=\"size_icon_item_collections\">20dp</dimen>\n\n    <dimen name=\"size_icon_error\">144dp</dimen>\n\n    <dimen name=\"error_dialog_min_width_button\">174dp</dimen>\n\n    <dimen name=\"width_error_dialog_message\">340dp</dimen>\n\n    <dimen name=\"margin_top_error_dialog_image\">46dp</dimen>\n    <dimen name=\"margin_bottom_error_dialog_image\">55dp</dimen>\n\n    <dimen name=\"header_wizard_height\">70dp</dimen>\n\n    <!-- ListPopupMenu -->\n    <dimen name=\"width_list_popup_menu\">220dp</dimen>\n    <dimen name=\"height_list_popup_menu\">320dp</dimen>\n\n    <!-- Fast Scroller -->\n    <dimen name=\"fastscroller_text_size\">40sp</dimen>\n    <dimen name=\"fastscroller_signal_size\">84dp</dimen>\n    <dimen name=\"fastscroller_bar_height\">76dp</dimen>\n    <dimen name=\"fastscroller_bar_width\">8dp</dimen>\n    <dimen name=\"fastscroller_signal_margin_right\">32dp</dimen>\n\n    <!-- PullToTabsView -->\n    <dimen name=\"pulltotabs_max_height\">140dp</dimen>\n    <dimen name=\"pulltotabs_height\">75dp</dimen>\n    <dimen name=\"pulltotabs_distance_change_tabs\">48dp</dimen>\n    <dimen name=\"pulltotabs_height_selected_line\">4dp</dimen>\n    <dimen name=\"pulltotabs_height_line\">1dp</dimen>\n\n    <!-- Profile -->\n    <dimen name=\"height_profile_user\">130dp</dimen>\n    <dimen name=\"size_icon_item_collection_checkbox_content\">58dp</dimen>\n    <dimen name=\"size_icon_item_collections_checkboxIcon\">18dp</dimen>\n\n    <!-- Publish Collection Wizard -->\n    <dimen name=\"publish_collection_message_width\">260dp</dimen>\n    <dimen name=\"publish_collection_message_height\">60dp</dimen>\n    <dimen name=\"publish_collection_field_height\">34dp</dimen>\n    <dimen name=\"publish_collection_form_padding\">0dp</dimen>\n    <dimen name=\"publish_collection_form_height\">232dp</dimen>\n    <dimen name=\"publish_collection_footer_height\">70dp</dimen>\n    <dimen name=\"publish_collection_footer_center\">30dp</dimen>\n    <dimen name=\"publish_collection_footer_end_margin\">20dp</dimen>\n    <dimen name=\"publish_collection_footer_end_height\">50dp</dimen>\n    <dimen name=\"publish_collection_size_pager\">8dp</dimen>\n    <dimen name=\"publish_collection_margin_pager\">4dp</dimen>\n\n    <!-- Edit Widgets -->\n    <dimen name=\"edit_widgets_size_icon\">48dp</dimen>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"view_type\" type=\"id\" />\n    <item name=\"position\" type=\"id\" />\n    <item name=\"use_layer_hardware\" type=\"id\" />\n    <item name=\"drawable_on\" type=\"id\" />\n    <item name=\"drawable_off\" type=\"id\" />\n    <item name=\"selected_item\" type=\"id\" />\n    <item name=\"fields_map\" type=\"id\" />\n    <item name=\"running_animation\" type=\"id\" />\n    <item name=\"app_widget_host_id\" type=\"id\" />\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources\n        xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n\n    <string name=\"app_name\">9 Cards</string>\n\n    <!-- General -->\n    <string name=\"edit\">Edit</string>\n    <string name=\"remove\">Remove</string>\n    <string name=\"moveTo\">Move to</string>\n    <string name=\"appInfo\">App Info</string>\n    <string name=\"uninstall\">Uninstall</string>\n    <string name=\"make_public\">Make Public</string>\n    <string name=\"share\">Share</string>\n    <string name=\"filter\">Filter</string>\n    <string name=\"loading\">Loading</string>\n    <string name=\"next\">Next</string>\n    <string name=\"contactUsError\">Oops! Please contact us if this problem persists.</string>\n    <string name=\"formFieldError\">Is it possible you missed filling in a section of the form?</string>\n    <string name=\"sendTo\">Send to</string>\n    <string name=\"buttonErrorReload\">Reload</string>\n\n    <string name=\"resize\">Resize</string>\n    <string name=\"move\">Move</string>\n    <string name=\"delete\">Delete</string>\n\n    <string name=\"itemAddedToCollectionSuccessful\">Item added to <xliff:g id=\"collection\" example=\"Social\">%1$s</xliff:g></string>\n\n    <!-- Menu -->\n    <string name=\"collectionsTitle\">Collections</string>\n    <string name=\"momentsTitle\">Moments</string>\n    <string name=\"profileTitle\">Profile</string>\n    <string name=\"wallpaperTitle\">Wallpaper</string>\n    <string name=\"nineCardsSettingsTitle\">Settings</string>\n    <string name=\"widgetsTitle\">Widgets</string>\n\n    <!-- Wizard -->\n    <string name=\"buttonContinue\">Continue</string>\n    <string name=\"buttonLetStart\">Let\\'s start</string>\n    <string name=\"buttonTryAgain\">Try again</string>\n    <string name=\"welcome\">\n        <![CDATA[\n            A launcher crafted for and by<br/><b>Android Power Users</b>\n        ]]>\n    </string>\n\n    <string name=\"termsAndConditions\">\n        <![CDATA[\n            by continuing, you accept the<br/><u>Terms of Use and Privacy Policy</u>.\n        ]]>\n    </string>\n\n    <string name=\"addDeviceTitle\">Hi, <xliff:g id=\"username\" example=\"Michael Reagan\">%1$s</xliff:g>!</string>\n    <string name=\"addDeviceMessage\">We have detected other devices in your user configuration. Do you want to load your existing Collections and themes from one of these devices?</string>\n\n    <string name=\"errorAccountsMessage\">You need a Google account to use 9Cards. Don\\'t worry; we only have access to your account name and any other permissions will be prompted.</string>\n    <string name=\"errorFineLocationMessage\">We\\'ll use your location to highlight the weather in the Moments screen.</string>\n\n    <string name=\"canceledGooglePermission\">The Android Market permission is required to enjoy <xliff:g id=\"app_name\">9Cards</xliff:g></string>\n\n    <string name=\"errorLoginUser\">Error accessing server. Please try again.</string>\n    <string name=\"errorConnectingGoogle\">Error connecting with Google. Please try again.</string>\n    <string name=\"goTo9Cards\">Welcome to 9Cards!</string>\n    <string name=\"downloadingYourSetup\">Just a second, we are downloading your setup.</string>\n\n    <string name=\"downloadingBitmapsMessage\">Downloading images for apps</string>\n    <string name=\"savingConfigurationMessage\">Saving configuration to our servers</string>\n\n    <string name=\"wizard_loading_connecting_with_google\">Connecting with Google</string>\n    <string name=\"wizard_loading_request_google_permission\">Request Google permission</string>\n    <string name=\"wizard_loading_connecting_with_plus\">Connecting with Google Plus</string>\n    <string name=\"wizard_loading_devices\">Loading devices</string>\n    <string name=\"wizard_loading_looking_for_better_collection\">Creating better Collections with your apps</string>\n    <string name=\"wizard_loading_saving_collections\">Saving collections</string>\n    <string name=\"wizard_loading_saving_moments\">Saving Moments</string>\n\n    <string name=\"creatingCollectionError\">We cannot create your Collections at this time. Please try again later.</string>\n\n    <string name=\"loadUserConfigDeviceReplace\">Replace <xliff:g id=\"phone\" example=\"Nexus 5\">%1$s</xliff:g> with a new configuration.</string>\n    <string name=\"deviceNotFoundMessage\">This device is not linked to your account. Please log in to continue.</string>\n\n    <string name=\"wizard_step_title_1\">Welcome back power user!</string>\n    <string name=\"wizard_step_1\">9Cards helps you to be more productive in your day-to-day life.</string>\n    <string name=\"wizard_step_title_2\">Remember, 9Cards is social!</string>\n    <string name=\"wizard_step_2\">Share your collections with your friends in the 9 Cards community.</string>\n    <string name=\"wizard_step_title_3\">Use Moments to adapt 9Cards to your life.</string>\n    <string name=\"wizard_step_3\">We designed Moments to adapt your phone to your lifestyle. Setup your moments with the widgets that you love.</string>\n    <string name=\"wizard_step_title_4\">Convert your smartphone into a really smart phone.</string>\n    <string name=\"wizard_step_4\">Dig into your Moments settings to configure WiFi and set hours so your phone will automatically switch Moments based on the timeline of your day.</string>\n    <string name=\"wizard_step_title_5\">Thanks for using 9Cards</string>\n    <string name=\"wizard_step_5\">Thanks for using 9Cards, power users! Remember that this is a free and Open Source project crafted by, and for the community. </string>\n\n    <string name=\"errorAndroidMarketPermissionNotAccepted\">You need to accept the Android Market permissions in order to use 9Cards.</string>\n    <string name=\"errorGooglePermissionNotAccepted\">You need to accept the Google permissions in order to use 9Cards.</string>\n\n    <string name=\"errorNoCollectionsSelected\">Please select the collections that you want to use.</string>\n\n    <string name=\"currentDeviceTitle\">%s (current device)</string>\n    <string name=\"newConfigurationSubtitle\">We\\'ll create a new configuration based on your apps</string>\n    <string name=\"deviceMigratedFromV1\">Migrated from the previous version</string>\n    <string name=\"otherDevicesLink\">Other devices configurations</string>\n\n    <string name=\"wizard_new_conf_steps_counter\">%1$s of 6</string>\n\n    <string name=\"wizard_new_conf_title_step_0\">Welcome to 9Cards</string>\n    <string name=\"wizard_new_conf_desc_step_0\">We\\'re looking to create the best user experience for you with this launcher and would like to know the things you\\'re interested in seeing.</string>\n\n    <string name=\"wizard_new_conf_title_step_1\">Apps grouped by categories</string>\n    <string name=\"wizard_new_conf_desc_step_1\">\n        <![CDATA[\n            We are categorizing your <b>%1$s apps</b> in <b>%2$s categories</b> that we call <b>Collections</b>. What categories would you like to use?\n        ]]>\n    </string>\n\n    <string name=\"wizard_new_conf_collection_counter_step_1\">%1$s of %2$s Collections</string>\n\n    <string name=\"wizard_new_conf_collection_name_step_1\">%1$s (%2$s apps)</string>\n    <string name=\"wizard_new_conf_collection_all_collections\">All Collections</string>\n\n    <string name=\"wizard_new_conf_title_step_2\">9Cards adapts to your day</string>\n    <string name=\"wizard_new_conf_desc_step_2\">\n        <![CDATA[\n            Do you use the same widgets when you work as when you\\'re listening to music, or during the evening? Help us discover how you want to use 9Cards. A <b>Moment</b> is named for every point in the day that you do different activities.\n        ]]>\n    </string>\n\n    <string name=\"wizard_new_conf_title_step_3\">Moments and WiFi</string>\n    <string name=\"wizard_new_conf_description_step_3\">Select your Moments and add the corresponding WiFi for each one, then we can show you why 9Cards is a better experience.</string>\n    <string name=\"wizard_new_conf_wifi_no_connected_step_3\">No WiFi in this moment</string>\n    <string name=\"wizard_new_conf_wifi_connected_step_3\">Connected to \\\"%1$s\\\"</string>\n\n    <string name=\"wizard_new_conf_title_step_4\">Which Moments are you interested in?</string>\n    <string name=\"wizard_new_conf_description_step_4\">Select the Moment you\\'re interested in.</string>\n\n    <string name=\"wizard_new_conf_title_step_5\">Thank you!</string>\n    <string name=\"wizard_new_conf_desc_step_5\">Remember, you can create and configure the Collections and Moments in the launcher. Enjoy 9Cards!</string>\n\n    <!-- Wizard Inline -->\n    <string name=\"wizard_inline_message_app_drawer\">Hi, Its %1$s from the 9 Cards team! Let me tell you about the App Drawer.</string> \n    <string name=\"wizard_inline_message_collections\">Hi, Its %1$s from the 9 Cards team! Check out the cool features in Collections.</string> \n    <string name=\"wizard_inline_message_profile\">Hi, Its %1$s from the 9 Cards team! Want to learn more about your Profile?</string> \n    <string name=\"wizard_inline_message_launcher\">Hi, Its %1$s from the 9 Cards team! I\\'d like to show you some cool things about the Launcher.</string> \n    <string name=\"wizard_inline_show\">Let\\'s go!</string>\n\n    <string name=\"wizard_inline_got_it\">Got it!</string>\n    <string name=\"wizard_inline_skip\">Skip</string>\n\n    <string name=\"wizard_inline_launcher_title_1\">Let me show you how to use 9Cards by Collections</string>\n    <string name=\"wizard_inline_launcher_1\">9Cards auto-categorizes your apps into smart Collections. We created your first Collections for you, but dig into My Collections to customize and get the most out of 9Cards.</string>\n\n    <string name=\"wizard_inline_launcher_title_2\">We are smarter together</string>\n    <string name=\"wizard_inline_launcher_2\">You’re now part of a huge family of Power Users. Explore different Collections created and shared by other uses in order to discover new ways to use your smartphone.</string>\n\n    <string name=\"wizard_inline_launcher_title_3\">Let me show you how to use 9Cards by Moments</string>\n    <string name=\"wizard_inline_launcher_3\">We called Moments to the parts in your day a day that use the phone for something in particular. Listen music, relax at your home, working at the office…</string>\n\n    <string name=\"wizard_inline_launcher_title_4\">Widgets adapted to fit your day-to-day life</string>\n    <string name=\"wizard_inline_launcher_4\">Widgets are the most productive part of Android, but perhaps, the most misunderstood too. Moments allow you to select the widgets associated with the particular moment in the day you’re in.</string>\n\n    <string name=\"wizard_inline_profile_title_1\">Publications</string>\n    <string name=\"wizard_inline_profile_1\">Take a look at your Public Collections and dig into their analytics.</string>\n\n    <string name=\"wizard_inline_profile_title_2\">Subscriptions</string>\n    <string name=\"wizard_inline_profile_2\">When you download a Public Collection, you can choose if you\\'d like to be updated when the user adds new apps, or whether you prefer to go your own way.</string>\n\n    <string name=\"wizard_inline_profile_title_3\">Accounts</string>\n    <string name=\"wizard_inline_profile_3\">Do you have more devices? No problem, you can synchronize 9Cards in them all. Enjoy 9Cards everywhere!</string>\n\n    <string name=\"wizard_inline_appdrawer_title_1\">App Drawer</string>\n    <string name=\"wizard_inline_appdrawer_1\">This screen, in particular, is one of our favorite parts of 9Cards. We worked hard to gather the most important information from your phone (Apps &amp; Contacts) in one spot.</string>\n\n    <string name=\"wizard_inline_appdrawer_title_2\">Everything is just a left slide away</string>\n    <string name=\"wizard_inline_appdrawer_2\">Take a look at the way we show you the information; you can access Apps and Contacts in a simple gesture and filter by Categories and Recently Installed or Favorites and Last Calls.</string>\n\n    <string name=\"wizard_inline_appdrawer_title_3\">Easy and Fast scroll</string>\n    <string name=\"wizard_inline_appdrawer_3\">Access your Apps and Contacts faster by using our Smart Scroll. Remember, you can drag any element to the Collection you like most.</string>\n\n    <string name=\"wizard_inline_collection_title_1\">Make your collections unique</string>\n    <string name=\"wizard_inline_collection_1\">You can adapt your collection with your personal preferences to improve your productivity superpowers. It\\'s simple to add Apps, Contacts, Shortcuts, or any of our Recommendations.</string>\n\n    <string name=\"wizard_inline_collection_title_2\">Swipe &amp; Drop</string>\n    <string name=\"wizard_inline_collection_2\">Easily navigate between collections with a swipe of your finger and change or edit your Collections by dragging and dropping your apps.</string>\n\n    <string name=\"wizard_inline_collection_title_3\">We are a community</string>\n    <string name=\"wizard_inline_collection_3\">Want to share your awesome Collections with your family and friends or with our community? Make your Collection public.</string>\n\n    <!-- Moment / Widgets -->\n\n    <string name=\"select_moment\">Select Moment</string>\n\n    <string name=\"addMoment\">Add Moment</string>\n    <string name=\"editMoment\">Edit Moment</string>\n    <string name=\"editMomentWithName\">Editing %1$s Moment</string>\n    <string name=\"changeMoment\">Change Moment</string>\n\n    <string name=\"linkCollection\">Quick Access to Collection</string>\n    <string name=\"hours\">Hours</string>\n    <string name=\"wifi\">WiFi</string>\n    <string name=\"bluetooth\">Bluetooth</string>\n\n    <string name=\"noLinkCollectionToMoment\">No link collection to this Moment</string>\n\n    <string name=\"addHoursToEditMoment\">Add time constraints to this Moment so it shows up in your launcher when you need it. </string>\n\n    <string name=\"addWifiToEditMoment\">Add WiFi constraints to this Moment and it\\'ll show when you are connected.</string>\n\n    <string name=\"addBluetoothToEditMoment\">Add Bluetooth constraints to this Moment and it will show when you are connected.</string>\n\n    <string name=\"linkCollectionMessage\">You can select a collection and have quick access to the apps, contacts and shortcuts inside of this collection from the right drawer in 9Cards. You only have to click the background or swipe from the right edge.</string>\n\n    <string name=\"widgetsErrorMessage\">There was a problem loading the widgets. Please try again.</string>\n\n    <string name=\"wifiDisconnected\">No Wi-Fi was detected. If you haven\\'t connected your Wi-Fi network, you should do so now.</string>\n\n    <string name=\"bluetoothDisconnected\">No Bluetooth Paired Devices were detected. If you haven\\'t connected your Bluetooth, you should do so now.</string>\n\n    <!-- Collections -->\n\n    <string name=\"createNewCollection\">Create new Collection</string>\n    <string name=\"myCollections\">My Collections</string>\n    <string name=\"publicCollections\">Public Collections</string>\n\n    <string name=\"newCollection\">New Collection</string>\n    <string name=\"editCollection\">Edit Collection</string>\n    <string name=\"collectionName\">Collection name</string>\n    <string name=\"selectIcon\">Select icon</string>\n    <string name=\"selectColor\">Select color</string>\n\n    <string name=\"addMyCollection\">Add to my Collections</string>\n    <string name=\"alreadyAddedCollection\">Already added</string>\n\n    <string name=\"removeCollectionMessage\">Are you sure you want to remove this Collection?</string>\n\n    <string name=\"minimumOneCollectionMessage\">You can\\'t remove the last Collection.</string>\n\n    <string name=\"emptyMoment\">You don\\'t have any Moments defined.</string>\n\n    <string name=\"momentNotFoundMessage\">Moment wasn\\'t found. You should create a new Moment from the My Collections section.</string>\n    <string name=\"noPhoneCallPermissionMessage\">Permission is needed to call your contact</string>\n\n    <!-- Collections Detail -->\n\n    <string name=\"applications\">Applications</string>\n    <string name=\"recommendations\">Recommendations</string>\n    <string name=\"contacts\">Contacts</string>\n    <string name=\"shortcuts\">Shortcuts</string>\n\n    <string name=\"itemsSelected\">%s selected</string>\n\n    <string name=\"unnamed\">Unnamed</string>\n\n    <string name=\"removeCardMessage\">Are you sure you want to remove this card?</string>\n    <string name=\"editCardDialogTitle\">Edit card</string>\n    <string name=\"editCardDialogName\">Name</string>\n\n    <string name=\"addApps\">Add Apps</string>\n    <string name=\"addRecommendations\">Add Recommendation</string>\n    <string name=\"addContact\">Add Contact</string>\n    <string name=\"addShortcut\">Add Shortcut</string>\n\n    <!-- Publish Collections Wizard -->\n\n    <string name=\"alreadyPublishedCollection\">Already published</string>\n\n    <string name=\"category\">Category</string>\n\n    <string name=\"publishCollectionHeader\">Publish your Collection</string>\n    <string name=\"publishCollectionMessage\">If you publish your Collections, other users can find new ways to use 9Cards.</string>\n    <string name=\"addInformationHeader\">Add information</string>\n    <string name=\"addInformationMessage\">Add information about your Collection so other users can find it.</string>\n    <string name=\"publishAction\">Publish</string>\n    <string name=\"publishingHeader\">Publishing</string>\n    <string name=\"publishingMessage\">Your Collection is being published</string>\n    <string name=\"addInformationcollectionName\">Add the Collection\\'s name</string>\n    <string name=\"addInformationCategory\">Select a category</string>\n    <string name=\"congratulationsHeader\">Congratulations!</string>\n    <string name=\"congratulationsMessage\">Your Collection has been published successfully </string>\n    <string name=\"shareCollection\">Share your Collection</string>\n    <string name=\"publishCollectionError\">Oops! A Collection without apps cannot be published.</string>\n    <string name=\"notPublishedCollectionError\">This Collection needs to be published before you can share it.</string>\n    <string name=\"publishingError\">Oops! An error has occurred publishing your Collection</string>\n    <string name=\"collectionError\">Oops! An error has occurred getting this Collection</string>\n    <string name=\"defaultUser\">User</string>\n\n    <!-- Apps Action -->\n\n    <string name=\"allApps\">All Apps</string>\n    <string name=\"appsByCategory\"><xliff:g id=\"apps_category\" example=\"Social apps\">%1$s</xliff:g> apps</string>\n    <string name=\"selectedApps\">%s selected apps</string>\n\n    <!-- Contacts Action -->\n\n    <string name=\"allContacts\">All Contacts</string>\n    <string name=\"favoriteContacts\">Favorite Contacts</string>\n\n    <string name=\"phones\">Phone</string>\n    <string name=\"sms\">SMS</string>\n    <string name=\"emails\">Email</string>\n\n    <string name=\"errorLoadingContacts\">Error loading contacts</string>\n    <string name=\"errorLoadingCalls\">Error loading last calls</string>\n    <string name=\"errorContactsPermission\">Contacts read permission needed. Do you want to try again?</string>\n    <string name=\"errorCallsPermission\">Read call log permission needed. Do you want to try again?</string>\n    <string name=\"errorLoadingApps\">Error loading apps</string>\n    <string name=\"errorLoadingShortcuts\">Error loading shortcuts</string>\n    <string name=\"errorLoadingRecommendations\">Error loading recommendations</string>\n\n    <string name=\"errorLoadingAddMoment\">Error loading your Moments</string>\n    <string name=\"errorSavingAddMoment\">Error saving your Moment</string>\n    <string name=\"emptyAddMoment\">You have added all your moments to your configuration</string>\n\n    <string name=\"errorLoadingPrivateCollections\">Error loading private Collections</string>\n    <string name=\"errorSavingPrivateCollections\">Error saving private Collections</string>\n    <string name=\"emptyPrivateCollections\">9Cards creates Collections from your applications and you already have all our recommended Collections. Try back another time to see more recomendations.</string>\n\n    <string name=\"errorLoadingPublicCollections\">Error loading public Collections</string>\n    <string name=\"errorSavingPublicCollections\">Error saving public Collections</string>\n    <string name=\"emptyPublicCollections\">There aren\\'t Collections published in this category. It\\'s the perfect time to share one of your Collections :-)</string>\n\n    <string name=\"sendEmailDialogChooserTitle\">Send email</string>\n\n    <!-- Contacts Info -->\n\n    <string name=\"generalInfo\">General Info</string>\n\n    <string name=\"phoneHome\">Home</string>\n    <string name=\"phoneMobile\">Mobile</string>\n    <string name=\"phoneWork\">Work</string>\n    <string name=\"phoneMain\">Main</string>\n    <string name=\"phoneFaxWork\">Work Fax</string>\n    <string name=\"phoneFaxHome\">Home Fax</string>\n    <string name=\"phonePager\">Pager</string>\n    <string name=\"phoneOther\">Other</string>\n\n    <string name=\"emailHome\">Home</string>\n    <string name=\"emailWork\">Work</string>\n    <string name=\"emailOther\">Other</string>\n\n    <!-- Recommendations Action -->\n\n    <string name=\"recommendationError\">We can\\'t help you with recommendations because this Collection doesn\\'t have applications yet.</string>\n\n    <string name=\"installNow\">Install now</string>\n    <string name=\"free\">Free</string>\n\n    <!-- Public Collections -->\n\n    <string name=\"top\">Top</string>\n    <string name=\"latest\">Latest</string>\n\n    <!-- Drawer -->\n    <string name=\"searchApps\">Search Apps…</string>\n    <string name=\"searchContacts\">Search Contacts…</string>\n\n    <string name=\"apps_alphabetical\">Alphabetical</string>\n    <string name=\"apps_categories\">By categories</string>\n    <string name=\"apps_date\">Recently installed</string>\n    <string name=\"contacts_alphabetical\">Alphabetical</string>\n    <string name=\"contacts_favorites\">Favorites</string>\n    <string name=\"contacts_last\">Last calls</string>\n    <string name=\"oneWeek\">1 week</string>\n    <string name=\"twoWeeks\">2 weeks</string>\n    <string name=\"oneMonth\">1 month</string>\n    <string name=\"twoMonths\">2 months</string>\n    <string name=\"fourMonths\">4 months</string>\n    <string name=\"sixMonths\">6 months</string>\n    <string name=\"moreOfTwoMonths\">More</string>\n\n    <string name=\"apps_not_found\">App not found. You can search apps in Google Play by clicking in the search button of your keyboard.</string>\n    <string name=\"contacts_not_found\">Contact wasn\\'t found in your agenda</string>\n    <string name=\"searching_in_google_play\">Searching in Google Play</string>\n    <string name=\"apps_not_found_in_google_play\">App wasn\\'t found in Google Play for this search</string>\n\n    <!-- Categories -->\n\n    <string name=\"all_categories\">Any Category</string>\n    <string name=\"all_apps\">All Apps</string>\n    <string name=\"misc\">Misc</string>\n\n    <string name=\"apps\">Apps</string>\n    <string name=\"game\">Games</string>\n    <string name=\"books_and_reference\">Books</string>\n    <string name=\"business\">Business</string>\n    <string name=\"comics\">Comics</string>\n    <string name=\"communication\">Communication</string>\n    <string name=\"education\">Education</string>\n    <string name=\"entertainment\">Entertainment</string>\n    <string name=\"finance\">Finance</string>\n    <string name=\"health_and_fitness\">Health</string>\n    <string name=\"libraries_and_demo\">Libraries &amp; Demo</string>\n    <string name=\"lifestyle\">Lifestyle</string>\n    <string name=\"app_wallpaper\">Live Wallpaper</string>\n    <string name=\"media_and_video\">Media</string>\n    <string name=\"medical\">Medical</string>\n    <string name=\"music_and_audio\">Music</string>\n    <string name=\"news_and_magazines\">News</string>\n    <string name=\"personalization\">Personalization</string>\n    <string name=\"photography\">Photography</string>\n    <string name=\"productivity\">Productivity</string>\n    <string name=\"shopping\">Shopping</string>\n    <string name=\"social\">Social</string>\n    <string name=\"sports\">Sports</string>\n    <string name=\"tools\">Tools</string>\n    <string name=\"transportation\">Transportation</string>\n    <string name=\"travel_and_local\">Travel</string>\n    <string name=\"weather\">Weather</string>\n    <string name=\"app_widgets\">Widgets</string>\n    <string name=\"video_players\">Video &amp; Editors</string>\n    <string name=\"maps_and_navigation\">Maps</string>\n    <string name=\"art_and_design\">ADesign</string>\n    <string name=\"auto_and_vehicles\">Auto and Vehicles</string>\n    <string name=\"beauty\">Beauty</string>\n    <string name=\"dating\">Dating</string>\n    <string name=\"events\">Events</string>\n    <string name=\"food_and_drink\">Foodie</string>\n    <string name=\"house_and_home\">Home</string>\n    <string name=\"parenting\">Parenting</string>\n\n    <string name=\"game_action\">Action</string>\n    <string name=\"game_adventure\">Adventure</string>\n    <string name=\"game_arcade\">Arcade</string>\n    <string name=\"game_board\">Board</string>\n    <string name=\"game_card\">Card</string>\n    <string name=\"game_casino\">Casino</string>\n    <string name=\"game_casual\">Casual</string>\n    <string name=\"game_educational\">Educational</string>\n    <string name=\"game_family\">Family</string>\n    <string name=\"game_wallpaper\">Live Wallpaper Games</string>\n    <string name=\"game_music\">Music</string>\n    <string name=\"game_puzzle\">Puzzle</string>\n    <string name=\"game_racing\">Racing</string>\n    <string name=\"game_role_playing\">Role Playing</string>\n    <string name=\"game_simulation\">Simulation</string>\n    <string name=\"game_sports\">Sports Games</string>\n    <string name=\"game_strategy\">Strategy</string>\n    <string name=\"game_trivia\">Trivia</string>\n    <string name=\"game_widgets\">Widgets Games</string>\n    <string name=\"game_word\">Word</string>\n\n    <!-- Moments -->\n    <string name=\"home\">Home</string>\n    <string name=\"homeDescription\">Apps &amp; Widgets to enjoy while you relax in your home</string>\n    <string name=\"work\">Work</string>\n    <string name=\"workDescription\">Apps &amp; Widgets for work to help increase productivity at your job</string>\n    <string name=\"night\">Night</string>\n    <string name=\"nightDescription\">Apps &amp; Widgets for watching films, tv series, or reading your favorite articles</string>\n    <string name=\"study\">Study</string>\n    <string name=\"studyDescription\">Apps &amp; Widgets for improving the time you spend learning</string>\n    <string name=\"music\">Music</string>\n    <string name=\"musicDescription\">Apps &amp; Widgets for enjoying your music, podcasts or the radio</string>\n    <string name=\"car\">Car</string>\n    <string name=\"carDescription\">Apps &amp; Widgets for helping you get home quickly</string>\n    <string name=\"sport\">Sport</string>\n    <string name=\"sportDescription\">Apps &amp; Widgets for thinking outside the box while you are working out</string>\n    <string name=\"out_and_about\">Out &amp; About</string>\n    <string name=\"out_and_aboutDescription\">Apps &amp; Widgets that you use when you are on the go.</string>\n\n    <!-- Profile -->\n    <string name=\"publications\">Publications</string>\n    <string name=\"subscriptions\">Subscriptions</string>\n    <string name=\"subscriptions_number\">%s subscriptions</string>\n    <string name=\"accounts\">Accounts</string>\n\n    <string name=\"syncHeaderDevices\">Other Devices</string>\n    <string name=\"syncCurrent\">Current device</string>\n    <string name=\"syncLastSynced\">Last synced: %s</string>\n\n    <string name=\"menuAccountSync\">Synchronize</string>\n    <string name=\"menuAccountCopy\">Copy</string>\n    <string name=\"menuAccountDelete\">Delete</string>\n    <string name=\"menuAccountChangeName\">Change name</string>\n    <string name=\"menuAccountPrintInfo\">Print info</string>\n\n    <string name=\"collectionAdded\">This Collection has been added to your Collections.</string>\n\n    <string name=\"syncingAccount\">Starting account synchronization</string>\n    <string name=\"accountSynced\">Account synced</string>\n\n    <string name=\"errorSyncing\">Error syncing account. Try again please.</string>\n\n    <string name=\"errorLoadingUser\">Error loading user</string>\n\n    <string name=\"errorEmptyNameForDevice\">Please, specify a name for the new configuration</string>\n    <string name=\"errorEmptyNameForDeviceButton\">Try again</string>\n\n    <string name=\"logout\">Logout</string>\n\n    <string name=\"copyAccountSyncDialogTitle\">New configuration</string>\n    <string name=\"renameAccountSyncDialogTitle\">New name</string>\n    <string name=\"hintNewConfigurationName\">Name</string>\n    <string name=\"removeAccountSyncMessage\">Are you sure that do you want remove this device?</string>\n    <string name=\"emptyAccountsMessage\">\n        <![CDATA[\n            There are no <b>accounts configured</b> in this Moment. Synchronization is disabled.\n        ]]>\n    </string>\n\n    <string name=\"errorLoadingPublishedCollections\">\n        <![CDATA[\n            There has been an error loading your <b>public Collections</b>\n        ]]>\n    </string>\n    <string name=\"emptyPublishedCollectionsMessage\">\n        <![CDATA[\n            There aren\\'t any <b>published</b> Collections. Maybe you can share one of <b>your Collections</b> :-)\n        ]]>\n    </string>\n\n    <string name=\"errorLoadingSubscriptions\">\n        <![CDATA[\n            There has been an error loading your <b>subscriptions</b>\n        ]]>\n    </string>\n    <string name=\"errorSubscribing\">Error subscribing to the collection</string>\n    <string name=\"errorUnsubscribing\">Error unsubscribing from the collection</string>\n    <string name=\"emptySubscriptionsMessage\">\n        <![CDATA[\n            You are not <b>subscribed</b> to any <b>public collections</b> in this Moment\n        ]]>\n    </string>\n\n    <string name=\"subscriptionActivated\">Subscription activated</string>\n    <string name=\"subscriptionDeactivated\">Subscription deactivated</string>\n\n    <!-- Preferences -->\n\n    <!-- Preferences Default Launcher -->\n    <string name=\"defaultLauncherTitle\">Default Launcher</string>\n    <string name=\"defaultLauncher\">Set as the default launcher</string>\n\n    <!-- Preferences Personalization -->\n    <string name=\"personalizationPreferences\">Personalization</string>\n\n    <!-- Theme Preferences -->\n\n    <string name=\"lookFeelPrefTitle\">Look &amp; feel</string>\n    <string name=\"lookFeelPrefSummary\">Change the theme and other graphic styles</string>\n\n    <string name=\"selectThemeTitle\">Select Theme</string>\n    <string name=\"selectThemeSummary\">Select Theme Summary</string>\n\n    <string name=\"themesDark\">Dark</string>\n    <string name=\"themesLight\">Light</string>\n\n    <string name=\"googleLogoTitle\">Google Logo</string>\n    <string name=\"googleLogoSummary\">Type of Google Logo on the Search Bar</string>\n\n    <string name=\"colouredGoogleLogo\">Colored</string>\n    <string name=\"themeColourGoogleLogo\">Theme\\'s Color</string>\n\n    <string name=\"fontsTitle\">Fonts Size</string>\n    <string name=\"fontsSummary\">General Fonts Size</string>\n\n    <string name=\"fontsSmall\">Small</string>\n    <string name=\"fontsMedium\">Medium</string>\n    <string name=\"fontsLarge\">Large</string>\n\n    <string name=\"iconsTitle\">Icons Size</string>\n    <string name=\"iconsSummary\">General Icons Size</string>\n\n    <string name=\"iconsSmall\">Small</string>\n    <string name=\"iconsMedium\">Medium</string>\n    <string name=\"iconsLarge\">Large</string>\n\n    <string name=\"cardPaddingTitle\">Card Padding</string>\n    <string name=\"cardPaddingSummary\">Padding between cards on Collections</string>\n\n    <string name=\"cardPaddingSmall\">Small</string>\n    <string name=\"cardPaddingMedium\">Medium</string>\n    <string name=\"cardPaddingLarge\">Large</string>\n\n    <!-- Moment Preferences -->\n\n    <string name=\"momentsPrefTitle\">Moments</string>\n    <string name=\"momentsPrefSummary\">Configure options for your Moments</string>\n\n    <string name=\"showMicSearchTitle\">Show Mic Search</string>\n    <string name=\"showMicSearchSummary\">Mic Search icon is now shown in the Moment bar</string>\n\n    <string name=\"showWeatherTitle\">Show Weather</string>\n    <string name=\"showWeatherSummary\">Weather icon is now shown in the Moment bar</string>\n\n    <!-- App Drawer Preferences -->\n\n    <string name=\"appDrawerPrefTitle\">App Drawer</string>\n    <string name=\"appDrawerPrefSummary\">Configure the Apps and Contacts Screen in your App Drawer</string>\n\n    <string name=\"appDrawerLongPressTitle\">Long Press Action</string>\n    <string name=\"appDrawerLongPressSummary\">Current Action: %s</string>\n\n    <string name=\"appDrawerOpenKeyboard\">Open keyboard</string>\n    <string name=\"appDrawerOpenContacts\">Open Contacts</string>\n\n    <string name=\"appDrawerOpenAnimationTitle\">Open Animation</string>\n    <string name=\"appDrawerOpenAnimationSummary\">Current Animation: %s</string>\n\n    <string name=\"appDrawerOpenAnimationReveal\">Circle reveal</string>\n    <string name=\"appDrawerOpenAnimationFade\">Fade</string>\n\n    <string name=\"appDrawerFistTabContactTitle\">Favorite Contacts First Tab</string>\n    <string name=\"appDrawerFistTabContactSummary\">Favorite Contacts will appear in the first position</string>\n\n    <string name=\"appDrawerSelectItemsInScrollerTitle\">Select Item During Scrolling</string>\n    <string name=\"appDrawerSelectItemsInScrollerSummary\">The items selected will be highlighted during scrolling on the list of Apps and Contacts</string>\n\n    <!-- Animations Preferences -->\n\n    <string name=\"animationsPrefTitle\">Animations</string>\n    <string name=\"animationsPrefSummary\">Select animations in 9Cards</string>\n\n    <string name=\"speedTitle\">Speed Animations</string>\n    <string name=\"speedSummary\">Speed that you can use for all animations in 9Cards</string>\n\n    <string name=\"speedsSlow\">Slow</string>\n    <string name=\"speedsNormal\">Normal</string>\n    <string name=\"speedsFast\">Fast</string>\n\n    <string name=\"collectionOpeningTitle\">Opening Collection</string>\n    <string name=\"collectionOpeningSummary\">Animation for opening Collection from 9Cards</string>\n\n    <string name=\"collectionOpeningsCircleReveal\">Circle Reveal</string>\n    <string name=\"collectionOpeningsNoAnimation\">No Animation</string>\n\n    <string name=\"workspaceAnimationTitle\">Workspace Animation</string>\n    <string name=\"workspaceAnimationSummary\">Animation between workspaces in main screen</string>\n\n    <string name=\"workspaceAnimationsHorizontalSlide\">Horizontal Slide</string>\n    <string name=\"workspaceAnimationsAppearsBehind\">Appears from behind</string>\n\n    <string name=\"wallpaperAnimationTitle\">Wallpaper movement</string> \n    <string name=\"wallpaperAnimationSummary\">The background image will move along with you as you swipe left or right through your home screens</string>\n\n    <!-- Analytics Preferences -->\n\n    <string name=\"analyticsPrefTitle\">Analytics</string>\n    <string name=\"analyticsPrefSummary\">Learn more about our analytics</string>\n\n    <string name=\"analyticsEnabledPrefTitle\">Enable Analytics</string>\n    <string name=\"analyticsEnabledPrefSummary\">9 Cards uses Google Analytics to track user behavior regarding how they open and use apps. This user data is anonymous and is collected for the purpose of generating statistics on popular apps, best collections, and improving your user experience. The data is only used for this purpose and will not be sold, published, or used in any other manner, other than to improve the functionality of this launcher. If you prefer that your anonymous data is not used, you may opt-out here</string>\n\n    <!-- Developer Preferences -->\n\n    <string name=\"developerOptionsActivated\">Developer options activated</string>\n\n    <string name=\"developerPrefTitle\">Developer Options</string>\n    <string name=\"developerPrefSummary\">Only use this if you are an Android/Scala Dev :-)</string>\n\n    <string name=\"devAwarenessAPICategory\">Awareness API</string>\n\n    <string name=\"devProbablyActivityTitle\">Probable Activity</string>\n    <string name=\"devHeadphonesTitle\">Headphones</string>\n    <string name=\"devLocationTitle\">Location</string>\n    <string name=\"devWeatherTitle\">Weather</string>\n\n    <string name=\"devLocalDatabaseCategory\">Local Database</string>\n\n    <string name=\"devAppsCategorizedTitle\">Apps Categorized</string>\n    <string name=\"devAppsCategorizedSummary\"><xliff:g id=\"apps_categorized\" example=\"24\">%1$s</xliff:g> of <xliff:g id=\"apps_total\" example=\"24\">%2$s</xliff:g> apps are categorized</string>\n\n    <string name=\"devAndroidTokenTitle\">Android Token</string>\n\n    <string name=\"devDeviceCloudIdTitle\">Device Cloud Id</string>\n\n    <string name=\"devDimensionsTitle\">Dimension</string>\n\n    <string name=\"devCurrentDimenTitle\">Current Dimension</string>\n\n    <string name=\"devCurrentDensityTitle\">Current Density</string>\n\n    <string name=\"devClickClipboard\">Click to copy to the clipboard</string>\n    <string name=\"devCopiedToClipboard\">Copied to clipboard</string>\n\n    <string name=\"devWizardTitle\">Wizard</string>\n\n    <string name=\"devEmptyDevicesV1WizardTitle\">V1 Api sent a empty devices</string>\n\n    <string name=\"devEmptyGoogleDriveDevicesWizardTitle\">Google Drive sent a empty devices</string>\n\n    <string name=\"devOthersTitle\">Others</string>\n\n    <string name=\"devClearCacheImagesTitle\">Clear cache images</string>\n\n    <string name=\"devShowPositionInCardsTitle\">Show positions in cards</string>\n\n    <string name=\"devCacheCleared\">Cache cleared</string>\n\n    <string name=\"devAppsListTitle\">Apps List</string>\n\n    <string name=\"collectionDetailAddCardsMessage\">\n        <![CDATA[\n            This collection doesn\\'t have cards. You can add <b>apps, contacts, recommendations, or shortcuts</b> from the + button\n        ]]>\n    </string>\n\n    <string name=\"devShowPrintInfoOptionInAccounts\">Print Drive information</string>\n\n    <string name=\"devIsStethoActiveFalse\">Enable Stetho</string>\n    <string name=\"devIsStethoActiveTrue\">Disable Stetho</string>\n    <string name=\"devIsFlowUpActiveFalse\">Enable FlowUp</string>\n    <string name=\"devIsFlowUpActiveTrue\">Disable FlowUp</string>\n    <string name=\"devIsStethoActiveSummary\">Requires restarting the application</string>\n    <string name=\"devIsFlowUpActiveSummary\">Requires restarting the application</string>\n    <string name=\"devRestartApplication\">Restart the application</string>\n    <string name=\"devBackendCategory\">Backend</string>\n    <string name=\"devOverrideBackendV2Url\">Override backend URL</string>\n    <string name=\"devOverrideBackendV2UrlSummary\">Requires restarting the application</string>\n    <string name=\"devBackendV2Url\">Backend URL</string>\n\n    <!-- Preferences About -->\n    <string name=\"others\">Others</string>\n\n    <string name=\"wizardInlineTitle\">Show Wizard Inline</string>\n    <string name=\"wizardInlineSummary\">We\\'ll share our personal tips again next time</string>\n    <string name=\"wizardInlineCleaned\">You\\'ll see the Wizard Inline again next time</string>\n\n    <string name=\"aboutTitle\">About 9Cards</string>\n    <string name=\"aboutSummary\">Want to know more about this Open Source project?</string>\n\n    <string name=\"sendFeedbackTitle\">Send Feedback</string> \n    <string name=\"sendFeedbackSummary\">We\\'d love to get your feedback on our app</string> \n\n    <string name=\"about_header_open_source\">9Cards is a free and open source Android launcher designed for and by Android power users</string> \n\n    <string name=\"about_github\">Visit 9Cards on GitHub</string> \n\n    <string name=\"rateGooglePlay\">Please rate us in Google Play.</string>\n\n    <string name=\"team_name\">Hi, It\\'s %1$s</string> \n\n    <string name=\"knowTeam\">Meet our team</string>\n    <string name=\"libraries\">Scala Libraries</string> \n    <string name=\"about_47_deg\">47 Degrees</string> \n    <string name=\"followTwitter\">Follow us on Twitter</string>\n    <string name=\"followFacebook\">Follow us on Facebook</string>\n    <string name=\"followGooglePlus\">Follow us on Google Plus</string>\n\n    <string name=\"only_client\">Client only</string> \n    <string name=\"only_server\">Server only</string> \n    <string name=\"server_and_client\">Server and client</string> \n\n    <string name=\"openSource\">We are Open Source!</string>\n    <string name=\"openSourceSummary\">Learn why this project is Open Source</string>\n    <string name=\"web_47deg\">47 Degrees Website</string>\n\n    <!-- Preferences Help -->\n    <string name=\"helpTitle\">Help</string>\n    <string name=\"helpSummary\">Can we help you with something?</string>\n\n    <!-- Shared Content -->\n    <string name=\"sharedIntentLabel\">Add card</string>\n    <string name=\"sharedContentDefaultTitle\">Web page</string>\n    <string name=\"sharedCardAdded\">Card added</string>\n    <string name=\"sharedContentErrorEmpty\">No content</string>\n    <string name=\"sharedContentErrorNotSupported\">Shared content not supported</string>\n    <string name=\"sharedContentErrorUnexpected\">Unexpected error</string>\n\n    <string name=\"sharedCollectionChangedNotificationTitle\">Collection updated!</string>\n    <string name=\"sharedCollectionChangedNotificationMsg\">The Collection \\'%s\\' has been updated</string>\n    <string name=\"sharedCollectionChangedNotificationUnsubscribe\">Unsubscribe</string>\n    <string name=\"sharedCollectionChangedNotificationSynchronize\">Synchronize</string>\n    <string name=\"sharedCollectionUnsubscribed\">Collection successfully unsubscribed</string>\n    <string name=\"sharedCollectionUpdated\">Collection successfully updated</string>\n\n    <plurals name=\"sharedCollectionChangedNotificationBigMsg\">\n        <item quantity=\"one\">The Collection \\'%s\\' has been updated with a new application. Click here to add it to your Collection.</item>\n        <item quantity=\"other\">The Collection \\'%s\\' has been updated with new applications. Click here to add them to your Collection.</item>\n    </plurals>\n\n    <!-- Edit Widgets -->\n    <string name=\"editingWidgets\">Editing widgets</string>\n    <string name=\"movingWidgets\">Moving widgets</string>\n    <string name=\"resizingWidgets\">Resizing widgets</string>\n    <string name=\"removeWidgetMessage\">Are you sure you want to remove this widget?</string>\n    <string name=\"noSpaceForWidget\">The widget doesn\\'t have space</string>\n    <string name=\"noMoveForWidget\">The widget can\\'t be moved there</string>\n    <string name=\"noResizeForWidget\">The widget can\\'t be resized there</string>\n\n    <!-- Moments -->\n    <string name=\"addDuplicateItemError\">Item is duplicated</string>\n    <string name=\"removeMomentMessage\">Are you sure you want to remove this Moment?</string>\n    <string name=\"cantRemoveOutAndAboutMoment\">You can\\'t remove Out &amp; About Moment</string>\n\n    <string name=\"message_moment_name\">%s Moment has special conditions</string> \n\n    <string name=\"specially_conditions_car\">The Car Moment is automatically activated when 9Cards detects that you\\'re traveling in a vehicle</string>\n    <string name=\"specially_conditions_music\">The Music Moment is automatically activated when you plug in your headphones</string> \n    <string name=\"specially_conditions_out_and_about\">The Out &amp; About Moment is automatically activated when there isn\\'t a better moment available for you (e.g: walking, on your way to your house, and so on)</string> \n\n    <!-- App Links -->\n    <string name=\"loadingCollection\">Retrieving collection details…</string>\n    <string name=\"linkNotSupportedError\">Link not supported</string>\n\n</resources>\n"
  },
  {
    "path": "modules/app/src/main/res/values/styles_actions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Base -->\n\n    <style name=\"BaseActionsRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:background\">@color/background_actions</item>\n    </style>\n\n    <style name=\"BaseActionsTransitionView\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"BaseActionsLoadingContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:visibility\">gone</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"BaseActionsLoadingText\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"BaseActionsLoading\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"BaseActionsContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:minHeight\">@dimen/min_height_actions_dialogs</item>\n    </style>\n\n    <style name=\"BaseActionsToolbarDialogParent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"BaseActionsToolbarDialog\" parent=\"BaseActionsToolbarDialogParent\"/>\n\n    <style name=\"BaseActionsToolbarRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"BaseActionsToolbar\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">?attr/actionBarSize</item>\n    </style>\n\n    <style name=\"BaseActionsToolbarTitle\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginTop\">@dimen/margin_top_fab_button</item>\n        <item name=\"android:textSize\">@dimen/text_xxlarge</item>\n        <item name=\"android:textColor\">@color/toolbar_title</item>\n        <item name=\"android:fontFamily\">sans-serif-medium</item>\n        <item name=\"android:layout_gravity\">top</item>\n    </style>\n\n    <style name=\"BaseActionsToolbarSearch\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginTop\">@dimen/margin_top_fab_button</item>\n        <item name=\"android:textSize\">@dimen/text_xxlarge</item>\n        <item name=\"android:textColor\">@color/toolbar_title</item>\n        <item name=\"android:fontFamily\">sans-serif-medium</item>\n        <item name=\"android:layout_gravity\">top</item>\n        <item name=\"android:background\">@android:color/transparent</item>\n        <item name=\"android:hint\">@string/searchApps</item>\n        <item name=\"android:imeOptions\">actionSearch</item>\n        <item name=\"android:inputType\">text</item>\n    </style>\n\n    <style name=\"BaseActionsToolbarExtendedContent\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginTop\">@dimen/margin_top_extended_content_toolbar</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:paddingRight\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"BaseActionsContentRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n    </style>\n\n    <style name=\"BaseActionsContentLayout\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"BaseActionsFabButton\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginTop\">@dimen/margin_top_fab_button</item>\n        <item name=\"android:layout_marginRight\">@dimen/margin_fab_button</item>\n        <item name=\"android:layout_gravity\">right</item>\n        <item name=\"borderWidth\">0dp</item>\n        <item name=\"elevation\">@dimen/elevation_fab_button</item>\n        <item name=\"pressedTranslationZ\">@dimen/elevation_fab_button_pressed</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"BaseActionsErrorContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_vertical</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <style name=\"BaseActionsErrorMessage\">\n        <item name=\"android:layout_width\">@dimen/width_error_dialog_message</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"BaseActionsErrorIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_error</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_error</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"BaseActionsErrorButton\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:minWidth\">@dimen/error_dialog_min_width_button</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:text\">@string/buttonErrorReload</item>\n        <item name=\"android:textColor\">@color/error_dialog_text_button</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_large</item>\n    </style>\n\n    <!-- Content -->\n\n    <style name=\"ActionsListContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"ActionsRecycler\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"ActionsFastScroll\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_gravity\">right</item>\n    </style>\n\n    <style name=\"ActionsViewTransition\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"AddAppsActionsRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"AddAppsMessage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:padding\">@dimen/padding_xlarge</item>\n    </style>\n\n    <style name=\"AddAppsSelectedMessageContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"AddAppsSelectedMessage\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_xlarge</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_default</item>\n        <item name=\"android:text\">@string/selectedApps</item>\n    </style>\n\n    <style name=\"AddAppsActionsRecycler\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:overScrollMode\">never</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:layout_marginRight\">@dimen/fastscroller_bar_width</item>\n        <item name=\"android:clipToPadding\">false</item>\n    </style>\n\n    <!-- Widgets -->\n\n    <style name=\"WidgetsActionsListContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"WidgetsActionsScrollMenu\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"WidgetsActionsMenu\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n    <style name=\"WidgetsActionsRecycler\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n    </style>\n\n    <!-- New / Edit Collection -->\n\n    <style name=\"NewCollectionRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"NewCollectionName\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_xxxlarge</item>\n        <item name=\"android:background\">@null</item>\n        <item name=\"android:hint\">@string/collectionName</item>\n        <item name=\"android:textColorHint\">@color/hint_collection_name</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:maxLength\">26</item>\n    </style>\n\n    <style name=\"NewCollectionSelectorContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_select_new_collection</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:paddingRight\">@dimen/padding_large</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"NewCollectionSelectorIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_select_new_collection</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_select_new_collection</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"NewCollectionSelectorTitle\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n    </style>\n\n    <!-- Edit Moment -->\n\n    <style name=\"AddMomentRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"AddMomentIcon\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"AddMomentTextContent\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"AddMomentName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_xxlarge</item>\n        <item name=\"android:paddingTop\">@dimen/padding_small</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"AddMomentDescription\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n    </style>\n\n    <!-- Edit Moment -->\n\n    <style name=\"EditMomentScroll\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"EditMomentRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:paddingTop\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"EditMomentItem\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"EditMomentHeaderContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"EditMomentBodyContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"EditMomentHeaderIcon\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"EditMomentHeaderIconCollection\" parent=\"EditMomentHeaderIcon\">\n        <item name=\"android:src\">@drawable/icon_edit_moment_link_collection</item>\n    </style>\n\n    <style name=\"EditMomentHeaderIconHours\" parent=\"EditMomentHeaderIcon\">\n        <item name=\"android:src\">@drawable/icon_edit_moment_hours</item>\n    </style>\n\n    <style name=\"EditMomentHeaderIconWifi\" parent=\"EditMomentHeaderIcon\">\n        <item name=\"android:src\">@drawable/icon_edit_moment_wifi</item>\n    </style>\n\n    <style name=\"EditMomentHeaderIconBluetooth\" parent=\"EditMomentHeaderIcon\">\n        <item name=\"android:src\">@drawable/icon_edit_moment_bluetooth</item>\n    </style>\n\n    <style name=\"EditMomentHeaderName\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"EditMomentHeaderCollectionName\" parent=\"EditMomentHeaderName\">\n        <item name=\"android:text\">@string/linkCollection</item>\n    </style>\n\n    <style name=\"EditMomentHeaderHoursName\" parent=\"EditMomentHeaderName\">\n        <item name=\"android:text\">@string/hours</item>\n    </style>\n\n    <style name=\"EditMomentHeaderWifiName\" parent=\"EditMomentHeaderName\">\n        <item name=\"android:text\">@string/wifi</item>\n    </style>\n\n    <style name=\"EditMomentHeaderBluetoothName\" parent=\"EditMomentHeaderName\">\n        <item name=\"android:text\">@string/bluetooth</item>\n    </style>\n\n    <style name=\"EditMomentHeaderAction\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"EditMomentHeaderActionInfo\" parent=\"EditMomentHeaderAction\">\n        <item name=\"android:src\">@drawable/icon_edit_moment_info</item>\n    </style>\n\n    <style name=\"EditMomentHeaderActionAdd\" parent=\"EditMomentHeaderAction\">\n        <item name=\"android:src\">@drawable/icon_edit_moment_add</item>\n    </style>\n\n    <style name=\"EditMomentHeaderSelectCollection\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:layout_margin\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"EditMomentHeaderSpecificMessageName\" parent=\"EditMomentHeaderName\">\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n    </style>\n\n    <style name=\"EditMomentHeaderSpecificMessage\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:layout_margin\">@dimen/padding_xlarge</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <!-- Edit Hour Moment -->\n\n    <style name=\"EditHourMomentRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:paddingTop\">@dimen/padding_large</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_large</item>\n        <item name=\"android:paddingRight\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"EditHourMomentContent\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"EditHourMomentHoursLine\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n    <style name=\"EditHourMomentHourContent\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_default</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"EditHourMomentHourText\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:gravity\">left</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"EditHourMomentHourLine\" parent=\"ActionsLine\">\n        <item name=\"android:layout_gravity\">bottom</item>\n    </style>\n\n    <style name=\"EditHourMomentDaysContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <!-- Edit Hour Moment -->\n\n    <style name=\"EditWifiMomentRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"EditWifiMomentWifiLine\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n    <style name=\"EditWifiMomentWifiText\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:gravity\">left</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_default</item>\n    </style>\n\n    <!-- Commons actions -->\n\n    <style name=\"ActionsActionDelete\" parent=\"EditMomentHeaderAction\">\n        <item name=\"android:src\">@drawable/icon_edit_moment_delete</item>\n    </style>\n\n    <style name=\"ActionsLine\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/divider_default</item>\n        <item name=\"android:tag\">line</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_cards.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"CardRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"CardContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"CardIconContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"CardIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_app_medium</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_app_medium</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"CardText\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">2</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_default</item>\n        <item name=\"android:paddingRight\">@dimen/padding_default</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:ellipsize\">end</item>\n    </style>\n\n    <style name=\"CardSelected\">\n        <item name=\"android:layout_width\">@dimen/size_selected_icon</item>\n        <item name=\"android:layout_height\">@dimen/size_selected_icon</item>\n        <item name=\"android:layout_gravity\">start</item>\n        <item name=\"android:layout_margin\">@dimen/padding_small</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"CardBadge\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">end</item>\n        <item name=\"android:layout_margin\">@dimen/padding_small</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_collections.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"CollectionsRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:fitsSystemWindows\">true</item>\n    </style>\n\n    <style name=\"CollectionsToolbarParent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_toolbar_collection_details</item>\n    </style>\n\n    <style name=\"CollectionsToolbar\" parent=\"CollectionsToolbarParent\" />\n\n    <style name=\"CollectionsToolbarTitle\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">top</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n        <item name=\"android:textColor\">@color/toolbar_title_collection_detail</item>\n    </style>\n\n    <style name=\"CollectionsIconContentParent\">\n        <item name=\"android:layout_width\">@dimen/size_icon_collection_detail</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_collection_detail</item>\n        <item name=\"android:background\">@drawable/background_icon_collection_detail</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_default</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:pivotX\">@dimen/pivot_x_icon_collection_detail</item>\n        <item name=\"android:pivotY\">0</item>\n    </style>\n\n    <style name=\"CollectionsIconContent\" parent=\"CollectionsIconContentParent\" />\n\n    <style name=\"CollectionsIcon\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:scaleType\">centerInside</item>\n    </style>\n\n    <style name=\"CollectionsPagerParent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_marginTop\">@dimen/margin_top_pagers_collection_details</item>\n    </style>\n\n    <style name=\"CollectionsPager\" parent=\"CollectionsPagerParent\" />\n\n    <style name=\"CollectionsTabsParent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_tabs_collection_details</item>\n        <item name=\"android:layout_marginTop\">@dimen/margin_top_tabs_collection_details</item>\n    </style>\n\n    <style name=\"CollectionsTabs\" parent=\"CollectionsTabsParent\" />\n\n    <style name=\"CollectionsTab\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_tabs</item>\n        <item name=\"android:textAllCaps\">true</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_default</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:paddingRight\">@dimen/padding_large</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <!-- Collection Detail -->\n\n    <style name=\"CollectionDetailLayout\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"CollectionDetailEmpty\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginTop\">@dimen/space_moving_collection_details</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_default</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_default</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_default</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"CollectionDetailEmptyContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:paddingTop\">@dimen/padding_empty_collection</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_empty_collection</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"CollectionEmptyMessage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:text\">@string/collectionDetailAddCardsMessage</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_empty_collection_message</item>\n        <item name=\"android:paddingRight\">@dimen/padding_empty_collection_message</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n        <item name=\"android:fontFamily\">sans-serif-regular</item>\n    </style>\n\n    <style name=\"CollectionEmptyImage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_xlarge</item>\n        <item name=\"android:src\">@drawable/empty_collection</item>\n    </style>\n\n    <style name=\"CollectionDetailPullToClose\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"CollectionDetailView\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:paddingTop\">@dimen/space_moving_collection_details</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_small</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_small</item>\n        <item name=\"android:paddingRight\">@dimen/padding_small</item>\n        <item name=\"android:clipToPadding\">false</item>\n        <item name=\"android:overScrollMode\">never</item>\n    </style>\n\n    <!-- FAB Menu -->\n\n    <style name=\"CollectionsFabMenuContentParent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:cropToPadding\">false</item>\n    </style>\n\n    <style name=\"CollectionsFabMenuContent\" parent=\"CollectionsFabMenuContentParent\"/>\n\n    <style name=\"CollectionsFabButton\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_margin\">@dimen/margin_fab_button</item>\n        <item name=\"android:layout_gravity\">end</item>\n        <item name=\"borderWidth\">0dp</item>\n        <item name=\"elevation\">@dimen/elevation_fab_button</item>\n        <item name=\"pressedTranslationZ\">@dimen/elevation_fab_button_pressed</item>\n        <item name=\"backgroundTint\">@color/collection_detail_fab_button_item</item>\n        <item name=\"rippleColor\">@color/collection_detail_fab_button_item_ripple</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_dialogs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Dialog Contact Info Header -->\n\n    <style name=\"ContactInfoHeaderRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/header_height</item>\n    </style>\n\n    <style name=\"ContactInfoHeaderAvatar\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:scaleType\">centerCrop</item>\n    </style>\n\n    <style name=\"ContactInfoHeaderContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:gravity\">bottom</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:background\">@color/background_header_content</item>\n    </style>\n\n    <style name=\"ContactInfoHeaderName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_xxxxlarge</item>\n        <item name=\"android:textColor\">@color/text_dialog_name</item>\n        <item name=\"android:fontFamily\">sans-serif-regular</item>\n    </style>\n\n    <!-- Dialog Contact Info -->\n\n    <style name=\"DialogContactInfoRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"DialogContactInfoValue\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">left</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:textColor\">@color/text_item_value</item>\n        <item name=\"android:fontFamily\">sans-serif-regular</item>\n    </style>\n\n    <style name=\"DialogContactInfoCategory\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">left</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:textColor\">@color/text_item_category</item>\n        <item name=\"android:fontFamily\">sans-serif-regular</item>\n    </style>\n\n    <style name=\"DialogContactInfoLineHorizontal\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/divider_default</item>\n        <item name=\"android:layout_marginLeft\">@dimen/size_icon_contact_dialog</item>\n        <item name=\"android:layout_gravity\">bottom</item>\n    </style>\n\n    <!-- Dialog Contact Info General -->\n\n    <style name=\"DialogContactInfoGeneralContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:paddingTop\">@dimen/padding_xlarge</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_xlarge</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"DialogContactInfoContentGeneralIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_contact_dialog</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"DialogContactInfoGeneralIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_contact_small</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_contact_small</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"DialogContactInfoGeneralContentValues\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <!-- Dialog Contact Info Phone -->\n\n    <style name=\"DialogContactInfoContentValues\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n    <style name=\"DialogContactInfoPhoneContent\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_default</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:layout_weight\">1</item>\n    </style>\n\n    <style name=\"DialogContactInfoPhoneIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_contact_dialog</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:src\">@drawable/dialog_contact_icon_phone</item>\n        <item name=\"android:scaleType\">center</item>\n    </style>\n\n    <style name=\"DialogContactInfoPhoneContentValues\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"DialogContactInfoSmsIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_contact_dialog</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:src\">@drawable/dialog_contact_icon_sms</item>\n        <item name=\"android:scaleType\">centerInside</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <!-- Dialog Contact Info Email -->\n\n    <style name=\"DialogContactInfoEmailContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_default</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"DialogContactInfoEmailIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_contact_dialog</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:src\">@drawable/dialog_contact_icon_email</item>\n        <item name=\"android:scaleType\">centerInside</item>\n    </style>\n\n    <style name=\"DialogContactInfoEmailContentValues\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_default</item>\n    </style>\n\n    <!-- Dialog Icon Info -->\n\n    <style name=\"DialogIconInfoRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"DialogIconInfoName\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:drawablePadding\">@dimen/padding_large</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"DialogIconInfoSelect\">\n        <item name=\"android:layout_width\">@dimen/size_icon_select_new_collection</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_select_new_collection</item>\n    </style>\n\n    <!-- Dialog Color Info -->\n\n    <style name=\"DialogColorInfoRoot\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"DialogColorInfoImage\">\n        <item name=\"android:layout_width\">@dimen/size_icon_color_dialog</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_color_dialog</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <!-- ListPopupMenu -->\n\n    <style name=\"ListPopupMenuText\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:textColor\">@color/item_list_popup_menu</item>\n    </style>\n\n    <!-- SelectMomentDialog -->\n\n    <style name=\"SelectMomentDialogRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"SelectMomentDialogContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:background\">@color/background_actions</item>\n        <item name=\"layout_behavior\">android.support.design.widget.BottomSheetBehavior</item>\n    </style>\n\n    <style name=\"SelectMomentItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"SelectMomentItemContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"SelectMomentItemIcon\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_default</item>\n        <item name=\"android:paddingRight\">@dimen/padding_default</item>\n        <item name=\"android:paddingTop\">@dimen/padding_large</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"SelectMomentItemText\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"SelectMomentItemAction\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"SelectMomentItemPin\" parent=\"SelectMomentItemAction\">\n        <item name=\"android:src\">@drawable/icon_action_pin</item>\n    </style>\n\n    <style name=\"SelectMomentItemEdit\" parent=\"SelectMomentItemAction\">\n        <item name=\"android:src\">@drawable/icon_action_edit</item>\n    </style>\n\n    <style name=\"SelectMomentItemDelete\" parent=\"SelectMomentItemAction\">\n        <item name=\"android:src\">@drawable/icon_action_delete</item>\n    </style>\n\n    <style name=\"SelectMomentItemLine\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/divider_default</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_large</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_large</item>\n    </style>\n\n    <!-- SelectCollectionDialog -->\n\n    <style name=\"SelectCollectionDialogRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"SelectCollectionDialogContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:background\">@color/background_actions</item>\n        <item name=\"layout_behavior\">android.support.design.widget.BottomSheetBehavior</item>\n    </style>\n\n    <style name=\"SelectCollectionItemContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"SelectCollectionItemIcon\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"SelectCollectionItemText\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_default</item>\n    </style>\n\n    <!-- Dialog Publish Collection Wizard  -->\n\n    <style name=\"PublishCollectionWizardRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardDialog\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n        <item name=\"android:paddingTop\">@dimen/padding_xlarge</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_xlarge</item>\n        <item name=\"android:paddingRight\">@dimen/padding_xlarge</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardHeader\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:textSize\">@dimen/text_xxlarge</item>\n        <item name=\"android:fontFamily\">sans-serif-medium</item>\n        <item name=\"android:textColor\">@android:color/black</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardStartHeader\" parent=\"PublishCollectionWizardHeader\">\n        <item name=\"android:text\">@string/publishCollectionHeader</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationHeader\" parent=\"PublishCollectionWizardHeader\">\n        <item name=\"android:text\">@string/addInformationHeader</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardPublishingHeader\" parent=\"PublishCollectionWizardHeader\">\n        <item name=\"android:text\">@string/publishingHeader</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardEndHeader\" parent=\"PublishCollectionWizardHeader\">\n        <item name=\"android:text\">@string/congratulationsHeader</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardMessage\">\n        <item name=\"android:layout_width\">@dimen/publish_collection_message_width</item>\n        <item name=\"android:layout_height\">@dimen/publish_collection_message_height</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardStartMessage\" parent=\"PublishCollectionWizardMessage\">\n        <item name=\"android:text\">@string/publishCollectionMessage</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationMessage\" parent=\"PublishCollectionWizardMessage\">\n        <item name=\"android:text\">@string/addInformationMessage</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardPublishingMessage\" parent=\"PublishCollectionWizardMessage\">\n        <item name=\"android:text\">@string/publishingMessage</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardEndMessage\" parent=\"PublishCollectionWizardMessage\">\n        <item name=\"android:text\">@string/congratulationsMessage</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardImage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:scaleType\">centerInside</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardImageStart\" parent=\"PublishCollectionWizardImage\">\n        <item name=\"android:src\">@drawable/publish_collection_wizard_start</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardImageEnd\" parent=\"PublishCollectionWizardImage\">\n        <item name=\"android:src\">@drawable/publish_collection_wizard_end</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationScroll\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationScrollContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationForm\" parent=\"PublishCollectionWizardImage\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/publish_collection_form_height</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:padding\">@dimen/publish_collection_form_padding</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationCollectionNameContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_xlarge</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n\n    <style name=\"PublishCollectionWizardInformationCollectionNameTag\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:gravity\">left|center_vertical</item>\n        <item name=\"android:text\">@string/collectionName</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationInput\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_xlarge</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationCollectionNameInputMessage\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/publish_collection_field_height</item>\n        <item name=\"android:inputType\">text</item>\n        <item name=\"android:hint\">@string/addInformationcollectionName</item>\n        <item name=\"android:background\">@android:color/transparent</item>\n        <item name=\"android:gravity\">left|center_vertical</item>\n        <item name=\"android:paddingLeft\">@dimen/publish_collection_form_padding</item>\n        <item name=\"android:paddingRight\">@dimen/publish_collection_form_padding</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationCategorySelectContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_xlarge</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationCategoryTag\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:gravity\">left|center_vertical</item>\n        <item name=\"android:text\">@string/category</item>\n    </style>\n\n\n    <style name=\"PublishCollectionWizardInformationCategorySelect\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationCategorySpinner\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">@dimen/publish_collection_field_height</item>\n        <item name=\"android:gravity\">left|center_vertical</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:text\">@string/addInformationCategory</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationCategoryIndicator\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:src\">@drawable/tab_menu_indicator</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:gravity\">right|center_vertical</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationLineHorizontal\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/divider_wide</item>\n        <item name=\"android:background\">@android:color/white</item>\n        <item name=\"android:layout_gravity\">bottom</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardLoadingContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/publish_collection_form_height</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <style name=\"PublishingLoading\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardStepsPaginationPanel\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/publish_collection_footer_height</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:layout_gravity\">bottom</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardFooter\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/publish_collection_footer_height</item>\n        <item name=\"android:layout_gravity\">bottom</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardStartFooter\" parent=\"PublishCollectionWizardFooter\">\n        <item name=\"android:gravity\">right</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationFooter\" parent=\"PublishCollectionWizardFooter\">\n        <item name=\"android:gravity\">right</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardEndFooter\" parent=\"PublishCollectionWizardFooter\">\n        <item name=\"android:layout_marginTop\">@dimen/publish_collection_footer_end_margin</item>\n        <item name=\"android:layout_height\">@dimen/publish_collection_footer_end_height</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardStartArrow\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:src\">@drawable/publish_collection_wizard_arrow</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardInformationButton\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:textAllCaps\">true</item>\n        <item name=\"android:text\">@string/publishAction</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardEndLineHorizontal\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/divider_default</item>\n        <item name=\"android:background\">@android:color/white</item>\n        <item name=\"android:layout_gravity\">top</item>\n    </style>\n\n    <style name=\"PublishCollectionWizardEndButton\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:textAllCaps\">true</item>\n        <item name=\"android:textColor\">@color/footer_text_color</item>\n        <item name=\"android:text\">@string/shareCollection</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"EditCardNameWrapper\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"EditCardName\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:inputType\">textAutoComplete</item>\n        <item name=\"android:hint\">@string/editCardDialogName</item>\n    </style>\n\n    <!-- App Links Activity -->\n    <style name=\"AppLinkDialogRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"AppLinkLoadingLayout\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"AppLinkLoading\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"AppLinkLoadingText\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:text\">@string/loadingCollection</item>\n    </style>\n\n    <style name=\"AppLinkCollectionLayout\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_drawer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"LauncherDrawerParent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:clickable\">true</item>\n        <item name=\"android:clipToPadding\">false</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:background\">@color/background_dialog</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"LauncherDrawerContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_search_box</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:layout_margin\">@dimen/padding_default</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:padding\">0dp</item>\n    </style>\n\n    <style name=\"LauncherDrawerInnerContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"LauncherDrawerMessage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:padding\">@dimen/padding_xlarge</item>\n    </style>\n\n    <style name=\"LauncherDrawerRecycler\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"LauncherDrawerLoading\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"LauncherDrawerFabButton\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginBottom\">@dimen/margin_app_drawer_fab_button</item>\n        <item name=\"android:layout_marginRight\">@dimen/margin_app_drawer_fab_button</item>\n        <item name=\"android:layout_gravity\">bottom|right</item>\n        <item name=\"borderWidth\">0dp</item>\n        <item name=\"elevation\">@dimen/elevation_fab_button</item>\n        <item name=\"pressedTranslationZ\">@dimen/elevation_fab_button_pressed</item>\n        <item name=\"backgroundTint\">@color/drawer_fab_button</item>\n        <item name=\"rippleColor\">@color/drawer_fab_button_ripple</item>\n    </style>\n\n    <!-- Animated View -->\n\n    <style name=\"LauncherDrawerAnimatedContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"LauncherDrawerAnimatedRipple\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"LauncherDrawerAnimatedIcon\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_vertical</item>\n        <item name=\"android:layout_margin\">@dimen/padding_large</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_fast_scroller.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"FastScrollerSignalParent\">\n        <item name=\"android:layout_width\">@dimen/fastscroller_signal_size</item>\n        <item name=\"android:layout_height\">@dimen/fastscroller_signal_size</item>\n        <item name=\"android:background\">@drawable/fastscroller_signal</item>\n        <item name=\"android:visibility\">gone</item>\n        <item name=\"android:layout_marginRight\">@dimen/fastscroller_signal_margin_right</item>\n        <item name=\"android:layout_gravity\">right</item>\n    </style>\n\n    <style name=\"FastScrollerSignal\" parent=\"FastScrollerSignalParent\" />\n\n    <style name=\"FastScrollerText\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:textSize\">@dimen/fastscroller_text_size</item>\n        <item name=\"android:textColor\">@color/fastscroller_text</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <style name=\"FastScrollerIcon\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_small</item>\n        <item name=\"android:textColor\">@color/fastscroller_text</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:drawablePadding\">@dimen/padding_small</item>\n    </style>\n\n    <style name=\"FastScrollerBarContent\">\n        <item name=\"android:layout_width\">@dimen/fastscroller_bar_width</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_large</item>\n        <item name=\"android:layout_gravity\">right</item>\n    </style>\n\n    <style name=\"FastScrollerBar\">\n        <item name=\"android:layout_width\">@dimen/fastscroller_bar_width</item>\n        <item name=\"android:layout_height\">@dimen/fastscroller_bar_height</item>\n        <item name=\"android:src\">@drawable/fastscroller_bar</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_items.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Header List Item -->\n\n    <style name=\"HeaderListRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_simple_category</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"HeaderListName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:textColor\">@color/text_default_title</item>\n    </style>\n\n    <!-- Simple Item (Apps and Contacts) -->\n\n    <style name=\"AppItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_app_item</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <style name=\"AppItemBackground\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_default</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_small</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_small</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_small</item>\n    </style>\n\n    <style name=\"AppItemContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <style name=\"AppItemIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_app_medium</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_app_medium</item>\n    </style>\n\n    <style name=\"AppItemName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:fontFamily\">sans-serif-condensed</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:textColor\">@color/text_default_subtitle</item>\n    </style>\n\n    <style name=\"AppSelectedContent\">\n        <item name=\"android:layout_width\">@dimen/size_selected_icon</item>\n        <item name=\"android:layout_height\">@dimen/size_selected_icon</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_small</item>\n        <item name=\"android:layout_gravity\">top|right</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"AppSelected\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:layout_margin\">@dimen/card_padding_medium</item>\n    </style>\n\n    <!-- Search -->\n\n    <style name=\"SearchItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_app_item</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"SearchItemIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_app_medium</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_app_medium</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"SearchItemInfoContent\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"SearchItemName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n    </style>\n\n    <style name=\"SearchItemBottomInfoContent\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"SearchItemStars\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"SearchItemDownloads\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n    </style>\n\n    <style name=\"SearchItemPrice\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n    </style>\n\n    <!-- Tab Item -->\n\n    <style name=\"TabItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"TabItemContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/pulltotabs_height</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:layout_gravity\">bottom</item>\n    </style>\n\n    <style name=\"TabItemLine\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/pulltotabs_height_line</item>\n        <item name=\"android:layout_gravity\">bottom</item>\n        <item name=\"android:background\">@color/pulltotabs_line</item>\n    </style>\n\n    <style name=\"TabItemSelectLine\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/pulltotabs_height_selected_line</item>\n        <item name=\"android:layout_gravity\">bottom</item>\n    </style>\n\n    <style name=\"TabItemIcon\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"TabItemName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:fontFamily\">sans-serif-condensed</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:textColor\">@color/text_default_subtitle</item>\n    </style>\n\n    <!-- Contacts -->\n\n    <style name=\"ContactItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_contact_item</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_default</item>\n        <item name=\"android:paddingRight\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"ContactItemContentIcon\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"ContactItemIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_contact</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_contact</item>\n        <item name=\"android:layout_margin\">@dimen/padding_small</item>\n    </style>\n\n    <style name=\"ContactItemFavorite\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:src\">@drawable/icon_contact_favorite</item>\n        <item name=\"android:layout_gravity\">right</item>\n    </style>\n\n    <style name=\"ContactItemName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:textColor\">@color/text_default_title</item>\n    </style>\n\n    <!-- Last Call -->\n\n    <style name=\"LastCallItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_contact_item</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_default</item>\n        <item name=\"android:paddingRight\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"LastCallItemIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_contact</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_contact</item>\n    </style>\n\n    <style name=\"LastCallContentInfo\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"LastCallItemName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:textColor\">@color/text_default_title</item>\n    </style>\n\n    <style name=\"LastCallContentTime\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"LastCallContentCalls\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n    <style name=\"LastCallItemHour\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_small</item>\n        <item name=\"android:textColor\">@color/text_default_subtitle</item>\n    </style>\n\n    <!-- Shortcut -->\n\n    <style name=\"ShortcutItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"ShortcutItemIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_shortcut</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_shortcut</item>\n    </style>\n\n    <style name=\"ShortcutItemName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_default</item>\n        <item name=\"android:textColor\">@color/text_default_title</item>\n    </style>\n\n    <!-- Recommendations Items -->\n\n    <style name=\"RecommendationsItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_small</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_small</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_small</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"RecommendationsItemContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"RecommendationsItemHeader\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:layout_margin\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"RecommendationsItemHeaderTitles\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"RecommendationsItemHeaderIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_recommendation</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_recommendation</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_default</item>\n        <item name=\"android:scaleType\">centerInside</item>\n    </style>\n\n    <style name=\"RecommendationsItemHeaderName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_small</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:textColor\">@color/text_default_title</item>\n    </style>\n\n    <style name=\"RecommendationsItemHeaderTitleDataContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"RecommendationsItemHeaderStars\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingRight\">@dimen/padding_small</item>\n    </style>\n\n    <style name=\"RecommendationsItemHeaderDownloads\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_medium</item>\n        <item name=\"android:textColor\">@color/text_default_subtitle</item>\n    </style>\n\n    <style name=\"RecommendationsItemHeaderTag\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_medium</item>\n        <item name=\"android:paddingTop\">@dimen/padding_small</item>\n    </style>\n\n    <style name=\"RecommendationsItemScreenshots\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_screenshot</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_large</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_large</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"RecommendationsItemScreenshotItem\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">@dimen/height_screenshot</item>\n    </style>\n\n    <style name=\"RecommendationsItemLine\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/divider_default</item>\n    </style>\n\n    <style name=\"RecommendationsItemInstallNow\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:paddingRight\">@dimen/padding_large</item>\n        <item name=\"android:paddingTop\">@dimen/padding_small</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_small</item>\n        <item name=\"android:text\">@string/installNow</item>\n        <item name=\"android:gravity\">left|center_vertical</item>\n        <item name=\"android:textAllCaps\">true</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:textColor\">@color/text_default_subtitle</item>\n    </style>\n\n    <!-- Private Collections Items -->\n\n    <style name=\"PrivateCollectionsItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_small</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_small</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_small</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"PrivateCollectionsItemContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"PrivateCollectionsItemHeader\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:layout_margin\">@dimen/padding_large</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"PrivateCollectionsItemHeaderIconContent\">\n        <item name=\"android:layout_width\">@dimen/size_icon_item_collections_content</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_item_collections_content</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"PrivateCollectionsItemHeaderIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_item_collections</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_item_collections</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"PrivateCollectionsItemHeaderName\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"PrivateCollectionsItemIconsRow\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_large</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_large</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"PrivateCollectionsItemLine\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/divider_default</item>\n    </style>\n\n    <style name=\"PrivateCollectionsItemAddCollection\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:paddingRight\">@dimen/padding_large</item>\n        <item name=\"android:paddingTop\">@dimen/padding_small</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_small</item>\n        <item name=\"android:text\">@string/addMyCollection</item>\n        <item name=\"android:gravity\">left|center_vertical</item>\n        <item name=\"android:textAllCaps\">true</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n    </style>\n\n    <!-- Public Collections Items -->\n\n    <style name=\"PublicCollectionsItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_small</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_small</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_small</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemHeader\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:layout_margin\">@dimen/padding_large</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:layout_gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemHeaderIconContent\">\n        <item name=\"android:layout_width\">@dimen/size_icon_item_collections_content</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_item_collections_content</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemHeaderIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_item_collections</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_item_collections</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemHeaderNames\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_default</item>\n        <item name=\"android:layout_gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemHeaderName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemHeaderAuthor\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemHeaderDownloads\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:includeFontPadding\">false</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemIconsApps\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_large</item>\n        <item name=\"flexWrap\">wrap</item>\n        <item name=\"justifyContent\">space_between</item>\n        <item name=\"alignItems\">flex_start</item>\n        <item name=\"alignContent\">flex_start</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemHeaderSubscriptions\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_large</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_large</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_large</item>\n        <item name=\"android:includeFontPadding\">false</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemLine\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/divider_default</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemActionsContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/footer_height_default</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemAddCollection\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:paddingRight\">@dimen/padding_large</item>\n        <item name=\"android:paddingTop\">@dimen/padding_small</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_small</item>\n        <item name=\"android:gravity\">left|center_vertical</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n    </style>\n\n    <style name=\"PublicCollectionsItemShareCollection\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:paddingRight\">@dimen/padding_large</item>\n        <item name=\"android:paddingTop\">@dimen/padding_small</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_small</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <!-- Subscriptions Items -->\n\n    <style name=\"SubscriptionsItemContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"SubscriptionsItem\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:layout_margin\">@dimen/padding_medium</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:layout_gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"SubscriptionsCollectionCheckBoxContent\">\n        <item name=\"android:layout_width\">@dimen/size_icon_item_collection_checkbox_content</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_item_collection_checkbox_content</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"SubscriptionsCollectionCheckBox\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"SubscriptionsItemIcon\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:layout_margin\">@dimen/padding_small</item>\n    </style>\n\n    <style name=\"SubscriptionsCollectionCheckBoxIconContent\">\n        <item name=\"android:layout_width\">@dimen/size_icon_item_collections_checkboxIcon</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_item_collections_checkboxIcon</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_small</item>\n        <item name=\"android:layout_gravity\">top|right</item>\n    </style>\n\n    <style name=\"SubscriptionsItemCheckboxIcon\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:layout_margin\">@dimen/card_padding_small</item>\n    </style>\n\n    <style name=\"SubscriptionsItemNames\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_default</item>\n        <item name=\"android:layout_gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"SubscriptionsItemName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n    </style>\n\n    <style name=\"SubscriptionsItemStatus\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n    </style>\n\n    <style name=\"SubscriptionsItemSubscribed\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <!-- Widget Items -->\n\n    <style name=\"WidgetItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_margin\">@dimen/padding_default</item>\n        <item name=\"android:foreground\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"WidgetItemContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:background\">@color/widgets_background_content</item>\n    </style>\n\n    <style name=\"WidgetItemPreview\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">@dimen/size_widget_item_preview</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:scaleType\">centerInside</item>\n        <item name=\"android:layout_margin\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"WidgetItemHeaderContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:padding\">@dimen/padding_medium</item>\n        <item name=\"android:background\">@color/widgets_background_header</item>\n    </style>\n\n    <style name=\"WidgetItemTitle\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:textColor\">@color/widgets_text</item>\n        <item name=\"android:fontFamily\">sans-serif-medium</item>\n    </style>\n\n    <style name=\"WidgetItemCells\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:textColor\">@color/widgets_text</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"LauncherRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"LauncherForeground\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"LauncherLoading\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"LauncherRootContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:clipToPadding\">false</item>\n    </style>\n\n    <style name=\"LauncherTopBox\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"LauncherCollectionActionsPanel\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_search_box</item>\n        <item name=\"android:layout_margin\">@dimen/padding_default</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"LauncherCollectionActionsRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"LauncherCollectionActionLayout\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"LauncherCollectionAction\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:background\">@null</item>\n        <item name=\"android:textColor\">@color/text_launcher_action</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:textAllCaps\">false</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:drawablePadding\">@dimen/padding_default</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_default</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_default</item>\n    </style>\n\n    <!-- Search Panel (App Drawer) -->\n\n    <style name=\"LauncherSearchPanel\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:padding\">0dp</item>\n    </style>\n\n    <style name=\"LauncherSearchDrawerPanel\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_search_box</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:padding\">0dp</item>\n    </style>\n\n    <style name=\"LauncherSearchHeader\">\n        <item name=\"android:layout_width\">@dimen/height_search_box</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:scaleType\">centerInside</item>\n    </style>\n\n    <style name=\"LauncherSearchHeaderLine\">\n        <item name=\"android:layout_width\">@dimen/divider_default</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:background\">@color/divider_color_search_box</item>\n    </style>\n\n    <style name=\"LauncherSearchEditText\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:background\">@null</item>\n        <item name=\"android:textColor\">@color/drawer_search_box</item>\n        <item name=\"android:textColorHint\">@color/drawer_search_box_hint</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_default</item>\n        <item name=\"android:imeOptions\">actionSearch</item>\n        <item name=\"android:inputType\">text</item>\n    </style>\n\n    <style name=\"LauncherSearchIcon\">\n        <item name=\"android:layout_width\">@dimen/height_search_box</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:scaleType\">centerInside</item>\n        <item name=\"android:src\">@drawable/icon_action_bar_options</item>\n    </style>\n\n    <!-- Collections Panel -->\n\n    <style name=\"LauncherCollectionsContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"LauncherCollectionsPanel\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_search_box</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:padding\">0dp</item>\n    </style>\n\n    <style name=\"LauncherCollectionsBurgerButton\">\n        <item name=\"android:layout_width\">@dimen/height_search_box</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:scaleType\">centerInside</item>\n    </style>\n\n    <style name=\"LauncherCollectionsGoogleButton\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:scaleType\">fitStart</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"LauncherCollectionsMicButton\">\n        <item name=\"android:layout_width\">@dimen/height_search_box</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:scaleType\">centerInside</item>\n    </style>\n\n    <!-- Moment Panel -->\n\n    <style name=\"LauncherMomentPanel\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_search_box</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"LauncherMomentInfoContent\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n    <style name=\"LauncherMomentIconContent\">\n        <item name=\"android:layout_width\">@dimen/width_icon_content_top_bar_moment</item>\n        <item name=\"android:layout_height\">@dimen/height_icon_content_top_bar_moment</item>\n        <item name=\"android:layout_gravity\">center_vertical</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_medium</item>\n    </style>\n\n    <style name=\"LauncherMomentIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_top_bar_moment</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_top_bar_moment</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"LauncherMomentText\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textColor\">@color/text_launcher_moment_panel</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n        <item name=\"android:layout_gravity\">center_vertical</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n    </style>\n\n    <style name=\"LauncherMomentClock\" parent=\"LauncherMomentText\">\n        <item name=\"android:paddingLeft\">@dimen/padding_small</item>\n    </style>\n\n    <style name=\"LauncherMomentWeather\">\n        <item name=\"android:layout_width\">@dimen/height_search_box</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:scaleType\">centerInside</item>\n        <item name=\"android:src\">@drawable/icon_weather_clear</item>\n    </style>\n\n    <style name=\"LauncherMomentUnpin\">\n        <item name=\"android:layout_width\">@dimen/height_search_box</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:scaleType\">centerInside</item>\n        <item name=\"android:src\">@drawable/icon_action_unpin</item>\n    </style>\n\n    <style name=\"LauncherMomentGoogleButton\">\n        <item name=\"android:layout_width\">@dimen/height_search_box</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:scaleType\">centerInside</item>\n        <item name=\"android:src\">@drawable/icon_google_search</item>\n    </style>\n\n    <style name=\"LauncherMomentMicButton\">\n        <item name=\"android:layout_width\">@dimen/height_search_box</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:scaleType\">centerInside</item>\n        <item name=\"android:src\">@drawable/icon_mic_search</item>\n    </style>\n\n\n    <!-- App Drawer -->\n\n    <style name=\"LauncherWorkSpacesLayout\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n    </style>\n\n    <style name=\"LauncherWorkSpaces\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"LauncherWorkSpacesEdgeLeft\">\n        <item name=\"android:layout_width\">@dimen/size_edge_workspace</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_gravity\">left</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"LauncherWorkSpacesEdgeRight\">\n        <item name=\"android:layout_width\">@dimen/size_edge_workspace</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_gravity\">right</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"LauncherDrawerPanel\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_default</item>\n        <item name=\"android:paddingRight\">@dimen/padding_default</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_large</item>\n        <item name=\"android:clipToPadding\">false</item>\n        <item name=\"android:layout_gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"LauncherDrawerRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"LauncherDrawerContentItem\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_weight\">1</item>\n    </style>\n\n    <style name=\"LauncherDrawerItem\">\n        <item name=\"android:layout_width\">@dimen/size_icon_app_drawer</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_app_drawer</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"LauncherDrawerAppsItem\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:src\">@drawable/icon_app_drawer</item>\n    </style>\n\n    <style name=\"LauncherPaginationPanel\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/size_launcher_pager</item>\n        <item name=\"android:layout_margin\">@dimen/padding_default</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n    <!-- Collection Item -->\n\n    <style name=\"LauncherCollectionItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"LauncherCollectionItemLayout\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"LauncherCollectionItemIconRoot\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"LauncherCollectionItemIconParent\">\n        <item name=\"android:layout_width\">@dimen/size_group_collection_medium</item>\n        <item name=\"android:layout_height\">@dimen/size_group_collection_medium</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"LauncherCollectionItemIcon\" parent=\"LauncherCollectionItemIconParent\" />\n\n    <style name=\"LauncherCollectionItemName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textColor\">@color/collection_group_name</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:lines\">2</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n    </style>\n\n    <!-- FAB Menu -->\n\n    <style name=\"LauncherFabMenuContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:paddingBottom\">@dimen/margin_bottom_fab_menu_content_collections</item>\n        <item name=\"android:cropToPadding\">false</item>\n    </style>\n\n    <style name=\"LauncherFabButton\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_margin\">@dimen/margin_fab_button</item>\n        <item name=\"android:layout_gravity\">end</item>\n        <item name=\"borderWidth\">0dp</item>\n        <item name=\"elevation\">@dimen/elevation_fab_button</item>\n        <item name=\"pressedTranslationZ\">@dimen/elevation_fab_button_pressed</item>\n    </style>\n\n    <!-- Collection Menu -->\n\n    <style name=\"LauncherCollectionMenuRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:clipToPadding\">false</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"LauncherCollectionMenuContentBottom\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">bottom|center_horizontal</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"LauncherCollectionMenuButton\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/text_large</item>\n        <item name=\"android:drawablePadding\">@dimen/padding_default</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:textColor\">@color/text_menu_name</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n    </style>\n\n    <style name=\"LauncherCollectionMenuButtonWallpaper\" parent=\"LauncherCollectionMenuButton\">\n        <item name=\"android:drawableTop\">@drawable/launcher_fab_button_item_wallpaper</item>\n        <item name=\"android:text\">@string/wallpaperTitle</item>\n    </style>\n\n    <style name=\"LauncherCollectionMenuButtonWidgets\" parent=\"LauncherCollectionMenuButton\">\n        <item name=\"android:drawableTop\">@drawable/launcher_fab_button_item_widgets</item>\n        <item name=\"android:text\">@string/widgetsTitle</item>\n    </style>\n\n    <style name=\"LauncherCollectionMenuButtonSetting\" parent=\"LauncherCollectionMenuButton\">\n        <item name=\"android:drawableTop\">@drawable/launcher_fab_button_item_settings</item>\n        <item name=\"android:text\">@string/nineCardsSettingsTitle</item>\n    </style>\n\n    <style name=\"LauncherCollectionMenuContent\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:gravity\">left</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:layout_marginBottom\">@dimen/size_workspace_menu_item</item>\n    </style>\n\n    <!-- Collections Workspace -->\n\n    <style name=\"LauncherCollectionsRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"LauncherCollectionsGrid\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"MenuDrawerLayout\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"MenuNavigationView\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_gravity\">start</item>\n    </style>\n\n    <style name=\"MenuAppsMomentContent\">\n        <item name=\"android:layout_width\">@dimen/size_apps_bar_moment</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_gravity\">end</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"MenuAppsMomentScroll\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"MenuAppsMomentApps\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"MenuAppsMomentIconContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_header_apps_bar_moment</item>\n    </style>\n\n    <style name=\"MenuAppsMomentIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_apps_bar_moment</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_apps_bar_moment</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"MenuHeaderRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/menu_height</item>\n        <item name=\"android:background\">@color/background_menu_header</item>\n    </style>\n\n    <style name=\"MenuHeaderContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:gravity\">bottom</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"MenuHeaderCover\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:scaleType\">centerCrop</item>\n    </style>\n\n    <style name=\"MenuHeaderAvatar\">\n        <item name=\"android:layout_width\">@dimen/menu_size_avatar</item>\n        <item name=\"android:layout_height\">@dimen/menu_size_avatar</item>\n    </style>\n\n    <style name=\"MenuHeaderName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:textColor\">@color/text_menu_name</item>\n        <item name=\"android:paddingTop\">@dimen/padding_large</item>\n        <item name=\"android:fontFamily\">sans-serif-medium</item>\n    </style>\n\n    <style name=\"MenuHeaderEmail\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_medium</item>\n        <item name=\"android:textColor\">@color/text_menu_email</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_menu_items.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Workspace Menu Item -->\n\n    <style name=\"WorkspaceMenuItem\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"WorkspaceMenuItemTitle\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:textColor\">@color/collection_workspace_button_item_title</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:padding\">@dimen/padding_small</item>\n    </style>\n\n    <style name=\"WorkspaceMenuItemIcon\">\n        <item name=\"android:layout_width\">@dimen/size_workspace_menu_item</item>\n        <item name=\"android:layout_height\">@dimen/size_workspace_menu_item</item>\n        <item name=\"android:layout_margin\">@dimen/padding_default</item>\n        <item name=\"android:scaleType\">centerInside</item>\n    </style>\n\n    <!-- WorkSpace Moment Icon -->\n\n    <style name=\"WorkSpaceMomentIcon\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:paddingTop\">@dimen/padding_medium</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_medium</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"WorkSpaceMomentIconTitle\">\n        <item name=\"android:layout_width\">@dimen/size_workspace_moment_icon</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingTop\">@dimen/padding_small</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:textSize\">@dimen/text_medium</item>\n        <item name=\"android:fontFamily\">sans-serif-condensed</item>\n    </style>\n\n    <style name=\"WorkSpaceMomentIconDrawableContent\">\n        <item name=\"android:layout_width\">@dimen/size_workspace_moment_icon</item>\n        <item name=\"android:layout_height\">@dimen/size_workspace_moment_icon</item>\n    </style>\n\n    <style name=\"WorkSpaceMomentIconDrawable\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <!-- Fab Menu Item -->\n\n    <style name=\"FabMenu\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">bottom|end</item>\n        <item name=\"android:gravity\">end</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"FabMenuItem\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n    <style name=\"FabMenuItemTitle\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textStyle\">bold</item>\n        <item name=\"android:background\">@drawable/background_title_fab_menu_item</item>\n        <item name=\"android:textColor\">@color/collection_fab_button_item_title</item>\n        <item name=\"android:padding\">@dimen/padding_small</item>\n    </style>\n\n    <style name=\"FabMenuItemIcon\">\n        <item name=\"android:layout_width\">@dimen/size_fab_menu_item</item>\n        <item name=\"android:layout_height\">@dimen/size_fab_menu_item</item>\n        <item name=\"android:layout_marginLeft\">@dimen/margin_horizontal_fab_button_item</item>\n        <item name=\"android:layout_marginRight\">@dimen/margin_horizontal_fab_button_item</item>\n        <item name=\"android:layout_marginTop\">@dimen/margin_vertical_fab_button_item</item>\n        <item name=\"android:layout_marginBottom\">@dimen/margin_vertical_fab_button_item</item>\n        <item name=\"android:scaleType\">centerInside</item>\n    </style>\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_preferences.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"PreferencesActionBar\" parent=\"@android:style/Widget.ActionBar\">\n        <item name=\"android:background\">@color/primary</item>\n        <item name=\"android:titleTextStyle\">@style/PreferencesTitleBar</item>\n        <item name=\"titleTextStyle\">@style/PreferencesTitleBar</item>\n        <item name=\"android:homeAsUpIndicator\">@drawable/icon_action_bar_arrow_back</item>\n        <item name=\"homeAsUpIndicator\">@drawable/icon_action_bar_arrow_back</item>\n    </style>\n\n    <style name=\"PreferencesTitleBar\" parent=\"@style/TextAppearance.AppCompat.Widget.ActionBar.Title\">\n        <item name=\"android:textSize\">@dimen/text_xxlarge</item>\n        <item name=\"android:textColor\">@color/toolbar_title</item>\n        <item name=\"android:fontFamily\">sans-serif-medium</item>\n        <item name=\"android:layout_gravity\">top</item>\n    </style>\n\n    <!-- About Header -->\n\n    <style name=\"AboutHeaderPreferenceRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"AboutHeaderPreferenceLogo\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:src\">@drawable/wizard_logo</item>\n    </style>\n\n    <style name=\"AboutHeaderPreferenceName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:textSize\">@dimen/text_xxlarge</item>\n        <item name=\"android:textStyle\">bold</item>\n        <item name=\"android:textColor\">@color/preference_about_title</item>\n        <item name=\"android:paddingTop\">@dimen/padding_large</item>\n        <item name=\"android:text\">@string/app_name</item>\n    </style>\n\n    <style name=\"AboutHeaderPreferenceVersion\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:textColor\">@color/preference_about_desc</item>\n    </style>\n\n    <style name=\"AboutHeaderPreferenceText\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:textColor\">@color/preference_about_desc</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:text\">@string/about_header_open_source</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"AboutHeaderPreferenceGitHub\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:textColor\">@color/preference_about_title</item>\n        <item name=\"android:drawableLeft\">@drawable/icon_github</item>\n        <item name=\"android:drawablePadding\">@dimen/padding_default</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:text\">@string/about_github</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <!-- About Team -->\n\n    <style name=\"AboutTeamPreferenceRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"AboutTeamPreferenceContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_profile.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"ProfileRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"ProfileCoordinator\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"ProfileAppBarBase\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"ProfileAppBar\" parent=\"ProfileAppBarBase\"/>\n\n    <style name=\"ProfileCollapsingToolbar\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"layout_scrollFlags\">scroll|exitUntilCollapsed|snap</item>\n    </style>\n\n    <style name=\"ProfileUserFrameLayout\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_profile_user</item>\n        <item name=\"android:layout_gravity\">bottom|center_horizontal</item>\n        <item name=\"layout_collapseMode\">parallax</item>\n        <item name=\"layout_collapseParallaxMultiplier\">0.3</item>\n    </style>\n\n    <style name=\"ProfileUserLinearLayout\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"ProfileUserAvatar\">\n        <item name=\"android:layout_width\">@dimen/menu_size_avatar</item>\n        <item name=\"android:layout_height\">@dimen/menu_size_avatar</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"ProfileUserName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:gravity\">bottom|center</item>\n        <item name=\"android:textColor\">@android:color/white</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:fontFamily\">sans-serif-medium</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"ProfileUserEmail\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_medium</item>\n        <item name=\"android:textColor\">@color/text_menu_email</item>\n    </style>\n    \n    <style name=\"ProfileTabLayout\" parent=\"Widget.Design.TabLayout\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">?attr/actionBarSize</item>\n        <item name=\"tabMode\">fixed</item>\n        <item name=\"tabGravity\">fill</item>\n        <item name=\"tabPaddingStart\">@dimen/padding_small</item>\n        <item name=\"tabPaddingEnd\">@dimen/padding_small</item>\n        <item name=\"tabIndicatorColor\">@android:color/white</item>\n        <item name=\"tabTextAppearance\">@style/ProfileTabText</item>\n    </style>\n\n    <style name=\"ProfileTabText\" parent=\"TextAppearance.Design.Tab\">\n        <item name=\"android:textColor\">@android:color/white</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n    </style>\n\n    <style name=\"ProfileEmpty\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_margin\">@dimen/padding_default</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"ProfileEmptyContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:paddingTop\">@dimen/padding_empty_collection</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_empty_collection</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"ProfileEmptyMessage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:text\">@string/collectionDetailAddCardsMessage</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_empty_collection_message</item>\n        <item name=\"android:paddingRight\">@dimen/padding_empty_collection_message</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n        <item name=\"android:fontFamily\">sans-serif-regular</item>\n    </style>\n\n    <style name=\"ProfileEmptyImage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_xlarge</item>\n        <item name=\"android:src\">@drawable/empty_collection</item>\n    </style>\n\n    <style name=\"ProfileRecyclerView\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:scrollbars\">none</item>\n        <item name=\"layout_behavior\">@string/appbar_scrolling_view_behavior</item>\n    </style>\n\n    <style name=\"ProfileRecyclerLoading\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n    \n    <style name=\"ProfileToolbar\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">?attr/actionBarSize</item>\n        <item name=\"android:background\">@android:color/transparent</item>\n    </style>\n\n    <style name=\"ProfileAccountsItemHeader\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:fontFamily\">sans-serif-medium</item>\n    </style>\n\n    <style name=\"ProfileAccountsItemIcon\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingRight\">@dimen/padding_xlarge</item>\n        <item name=\"android:src\">@drawable/icon_account_mobile</item>\n    </style>\n\n    <style name=\"ProfileAccountsItemLayout\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"ProfileAccountsItemLayoutData\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"ProfileAccountsItemTitle\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n        <item name=\"android:fontFamily\">sans-serif-regular</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n    </style>\n\n    <style name=\"ProfileAccountsItemSubtitle\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:fontFamily\">sans-serif-regular</item>\n    </style>\n\n    <style name=\"ProfileAccountsItemAction\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:paddingRight\">@dimen/padding_large</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"ProfileLoading\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"ProfileConfigurationNameDialog\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"ProfileConfigurationNameWrapper\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"ProfileConfigurationName\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:inputType\">textAutoComplete</item>\n        <item name=\"android:hint\">@string/hintNewConfigurationName</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_pull_to_tabs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"PullToTabsContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"PullToTabsItem\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/pulltotabs_max_height</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"PullToTabsRecycler\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:overScrollMode\">never</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:layout_marginRight\">@dimen/fastscroller_bar_width</item>\n        <item name=\"android:clipToPadding\">false</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/styles_wizard.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"WizardRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:fitsSystemWindows\">true</item>\n    </style>\n\n    <!-- Loading -->\n\n    <style name=\"WizardLoadingContent\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <style name=\"WizardLoadingBar\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"WizardLoadingMessage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_xlarge</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n    </style>\n\n    <!-- User -->\n\n    <style name=\"WizardUserContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <style name=\"WizardUserLogo\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:src\">@drawable/wizard_logo</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:layout_marginTop\">@dimen/wizard_margin_top_logo</item>\n        <item name=\"android:visibility\">invisible</item>\n    </style>\n\n    <style name=\"WizardUserTitle\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:textSize\">@dimen/text_xxxlarge</item>\n        <item name=\"android:textColor\">@color/wizard_text_title</item>\n        <item name=\"android:gravity\">top|center_horizontal</item>\n        <item name=\"android:visibility\">invisible</item>\n    </style>\n\n    <style name=\"WizardUserButton\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:minWidth\">@dimen/wizard_min_width_button</item>\n        <item name=\"android:padding\">@dimen/padding_xlarge</item>\n        <item name=\"android:text\">@string/buttonLetStart</item>\n        <item name=\"android:textAllCaps\">true</item>\n        <item name=\"android:textColor\">@color/wizard_text_button_on</item>\n        <item name=\"android:visibility\">invisible</item>\n    </style>\n\n    <style name=\"WizardUserTerms\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:paddingRight\">@dimen/padding_large</item>\n        <item name=\"android:paddingTop\">@dimen/padding_large</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_xlarge</item>\n        <item name=\"android:textColor\">@color/primary</item>\n        <item name=\"android:checked\">true</item>\n        <item name=\"android:visibility\">invisible</item>\n    </style>\n\n    <!-- Devices -->\n\n    <style name=\"WizardDeviceContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <style name=\"WizardDeviceTitle\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:textSize\">@dimen/text_xxxlarge</item>\n        <item name=\"android:text\">@string/welcome</item>\n        <item name=\"android:textColor\">@color/wizard_text_title</item>\n    </style>\n\n    <style name=\"WizardDeviceMessage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:text\">@string/addDeviceMessage</item>\n        <item name=\"android:textColor\">@color/wizard_text_message</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"WizardDeviceRadioGroupScroll\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"WizardDeviceRadioGroup\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:textColor\">@color/wizard_text_title</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n    </style>\n\n    <style name=\"WizardDeviceButton\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:minWidth\">@dimen/wizard_min_width_button</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:text\">@string/buttonContinue</item>\n        <item name=\"android:textAllCaps\">true</item>\n        <item name=\"android:textColor\">@color/wizard_text_button_on</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_large</item>\n    </style>\n\n    <!-- Steps: Loading previous configuration -->\n\n    <style name=\"WizardStepsContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"WizardStepsWorkspace\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n    </style>\n\n    <style name=\"WizardBackgroundContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/wizard_height_image_content</item>\n    </style>\n\n    <style name=\"WizardStepsButton\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:minWidth\">@dimen/wizard_min_width_button</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:text\">@string/goTo9Cards</item>\n        <item name=\"android:textAllCaps\">true</item>\n        <item name=\"android:textColor\">@color/wizard_text_button</item>\n        <item name=\"android:layout_gravity\">bottom|center_horizontal</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"WizardStepsDownloadingMessage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:text\">@string/downloadingYourSetup</item>\n        <item name=\"android:textSize\">@dimen/text_small</item>\n        <item name=\"android:textColor\">@color/wizard_text_downloading</item>\n        <item name=\"android:layout_gravity\">bottom|center_horizontal</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:visibility\">gone</item>\n    </style>\n\n    <style name=\"WizardStepsPaginationPanel\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_xlarge</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:layout_gravity\">bottom|center_horizontal</item>\n    </style>\n\n    <!-- Step Item -->\n\n    <style name=\"WizardStepItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"WizardStepItemImageContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/wizard_height_image_content</item>\n    </style>\n\n    <style name=\"WizardStepItemImage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"WizardStepItemTitle\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:maxWidth\">@dimen/wizard_max_width_step_title</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:textSize\">@dimen/text_xxxlarge</item>\n        <item name=\"android:lines\">2</item>\n        <item name=\"android:ellipsize\">end</item>\n    </style>\n\n    <style name=\"WizardStepItemMessage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:maxWidth\">@dimen/wizard_max_width_step_message</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n        <item name=\"android:textColor\">@color/wizard_text_step_item_message</item>\n        <item name=\"android:lines\">3</item>\n        <item name=\"android:ellipsize\">end</item>\n    </style>\n\n    <!-- Wizard Checkbox -->\n\n    <style name=\"WizardCheckboxRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"WizardCheckboxIcon\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"WizardCheckboxText\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <!-- Wizard Wifi Checkbox -->\n\n    <style name=\"WizardWifiCheckboxRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:paddingTop\">@dimen/padding_large</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_large</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_default</item>\n        <item name=\"android:paddingRight\">@dimen/padding_default</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"WizardWifiCheckboxIcon\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_large</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"WizardWifiCheckboxNamesRoot\">\n        <item name=\"android:layout_width\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"WizardWifiCheckboxName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n    </style>\n\n    <style name=\"WizardWifiCheckboxConnectedText\">\n        <item name=\"android:layout_width\">@dimen/wizard_min_width_checkbox_wifi</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:lines\">1</item>\n        <item name=\"android:ellipsize\">end</item>\n    </style>\n\n    <style name=\"WizardWifiCheckboxLine\">\n        <item name=\"android:layout_width\">@dimen/stroke_thin</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:background\">@color/wizard_line</item>\n        <item name=\"android:layout_margin\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"WizardWifiCheckboxSelectWifi\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:src\">@drawable/icon_edit_moment_wifi</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <!-- Wizard Moment Checkbox -->\n\n    <style name=\"WizardMomentCheckboxRoot\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"WizardMomentCheckboxIconContent\">\n        <item name=\"android:layout_width\">@dimen/wizard_size_moment_checkbox</item>\n        <item name=\"android:layout_height\">@dimen/wizard_size_moment_checkbox</item>\n        <item name=\"android:layout_margin\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"WizardMomentCheckboxIcon\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"WizardMomentCheckboxTagContent\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">right</item>\n    </style>\n\n    <style name=\"WizardMomentCheckboxTag\">\n        <item name=\"android:layout_width\">@dimen/wizard_size_checkbox_title</item>\n        <item name=\"android:layout_height\">@dimen/wizard_size_checkbox_title</item>\n        <item name=\"android:layout_margin\">@dimen/stroke_default</item>\n    </style>\n\n    <style name=\"WizardMomentCheckboxName\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_xxlarge</item>\n        <item name=\"android:textColor\">@color/wizard_text_title</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_large</item>\n    </style>\n\n    <!-- New configuration -->\n\n    <style name=\"WizardNewConfigurationRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationStepContentRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationStepContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationHeaderContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/wizard_height_header_steps</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationHeaderImage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationTitle\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_xxxxlarge</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n        <item name=\"android:padding\">@dimen/padding_xxlarge</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationDescription\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n        <item name=\"android:textColor\">@color/wizard_text_title</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_xxlarge</item>\n        <item name=\"android:paddingRight\">@dimen/padding_xxlarge</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationPaginationContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_xlarge</item>\n        <item name=\"android:paddingRight\">@dimen/padding_default</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_xlarge</item>\n        <item name=\"android:paddingTop\">@dimen/padding_xlarge</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationPagers\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_large</item>\n        <item name=\"android:textColor\">@color/wizard_checkbox_unselected</item>\n        <item name=\"android:layout_gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationNext\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:orientation\">horizontal</item>\n        <item name=\"android:layout_gravity\">center_vertical|end</item>\n        <item name=\"android:gravity\">center_vertical</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationNextText\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n        <item name=\"android:text\">@string/next</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_default</item>\n        <item name=\"android:textAllCaps\">true</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationNextIcon\">\n        <item name=\"android:layout_width\">@dimen/size_icon_app_small</item>\n        <item name=\"android:layout_height\">@dimen/size_icon_app_small</item>\n    </style>\n\n    <!-- Step 0 -->\n\n    <style name=\"WizardNewConfigurationHeaderContentStep0\" parent=\"WizardNewConfigurationHeaderContent\">\n        <item name=\"android:background\">@color/wizard_new_conf_accent_1</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationHeaderImageStep0\" parent=\"WizardNewConfigurationHeaderImage\">\n        <item name=\"android:src\">@drawable/placeholder_step_0</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationTitleStep0\" parent=\"WizardNewConfigurationTitle\">\n        <item name=\"android:text\">@string/wizard_new_conf_title_step_0</item>\n        <item name=\"android:textColor\">@color/wizard_new_conf_accent_1</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationScrollStep0\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationDescriptionStep0\" parent=\"WizardNewConfigurationDescription\">\n        <item name=\"android:text\">@string/wizard_new_conf_desc_step_0</item>\n    </style>\n\n    <!-- Step 1 -->\n\n    <style name=\"WizardNewConfigurationTitleStep1\" parent=\"WizardNewConfigurationTitle\">\n        <item name=\"android:text\">@string/wizard_new_conf_title_step_1</item>\n        <item name=\"android:textColor\">@color/wizard_new_conf_accent_1</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationDescriptionStep1\" parent=\"WizardNewConfigurationDescription\"/>\n\n    <style name=\"WizardNewConfigurationHeaderContentStep1\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:paddingRight\">@dimen/padding_large</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationHeaderCheckboxStep1\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:padding\">@dimen/padding_default</item>\n        <item name=\"android:layout_gravity\">center_vertical</item>\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationHeaderCollectionCountStep1\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n        <item name=\"android:textColor\">@color/wizard_checkbox_unselected</item>\n        <item name=\"android:layout_gravity\">end|center_vertical</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationHeaderLineStep1\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/stroke_thin</item>\n        <item name=\"android:background\">@color/wizard_line</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_xxlarge</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_xxlarge</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationScrollStep1\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_default</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationScrollContentStep1\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_default</item>\n        <item name=\"android:paddingRight\">@dimen/padding_default</item>\n        <item name=\"android:paddingTop\">@dimen/padding_large</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_large</item>\n        <item name=\"android:clipToPadding\">false</item>\n    </style>\n\n    <!-- Step 2 -->\n\n    <style name=\"WizardNewConfigurationHeaderContentStep2\" parent=\"WizardNewConfigurationHeaderContent\">\n        <item name=\"android:background\">@color/wizard_new_conf_accent_2</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationHeaderImageStep2_1\" parent=\"WizardNewConfigurationHeaderImage\">\n        <item name=\"android:src\">@drawable/placeholder_moment_work_step_03</item>\n        <item name=\"android:layout_marginRight\">@dimen/padding_xxxlarge</item>\n        <item name=\"android:layout_marginBottom\">@dimen/padding_xxlarge</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationHeaderImageStep2_2\" parent=\"WizardNewConfigurationHeaderImage\">\n        <item name=\"android:src\">@drawable/placeholder_moment_night_step_03</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_xxlarge</item>\n        <item name=\"android:layout_marginLeft\">@dimen/padding_xxxlarge</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationTitleStep2\" parent=\"WizardNewConfigurationTitle\">\n        <item name=\"android:text\">@string/wizard_new_conf_title_step_2</item>\n        <item name=\"android:textColor\">@color/wizard_new_conf_accent_2</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationScrollStep2\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationDescriptionStep2\" parent=\"WizardNewConfigurationDescription\"/>\n\n    <!-- Step 3 -->\n\n    <style name=\"WizardNewConfigurationTitleStep3\" parent=\"WizardNewConfigurationTitle\">\n        <item name=\"android:text\">@string/wizard_new_conf_title_step_3</item>\n        <item name=\"android:textColor\">@color/wizard_new_conf_accent_2</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationDescriptionStep3\" parent=\"WizardNewConfigurationDescription\">\n        <item name=\"android:text\">@string/wizard_new_conf_description_step_3</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationScrollStep3\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_xlarge</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationScrollContentStep3\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_default</item>\n        <item name=\"android:paddingRight\">@dimen/padding_default</item>\n    </style>\n\n    <!-- Step 4 -->\n\n    <style name=\"WizardNewConfigurationTitleStep4\" parent=\"WizardNewConfigurationTitle\">\n        <item name=\"android:text\">@string/wizard_new_conf_title_step_4</item>\n        <item name=\"android:textColor\">@color/wizard_new_conf_accent_3</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationDescriptionStep4\" parent=\"WizardNewConfigurationDescription\">\n        <item name=\"android:text\">@string/wizard_new_conf_description_step_4</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationContentMomentsStep4\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:orientation\">vertical</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationCheckStep4\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <!-- Step 5 -->\n\n    <style name=\"WizardNewConfigurationHeaderContentStep5\" parent=\"WizardNewConfigurationHeaderContent\">\n        <item name=\"android:background\">@color/wizard_new_conf_accent_4</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationHeaderImageStep5\" parent=\"WizardNewConfigurationHeaderImage\">\n        <item name=\"android:src\">@drawable/placeholder_step_06</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationTitleStep5\" parent=\"WizardNewConfigurationTitle\">\n        <item name=\"android:text\">@string/wizard_new_conf_title_step_5</item>\n        <item name=\"android:textColor\">@color/wizard_new_conf_accent_4</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationScrollStep5\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationDescriptionStep5\" parent=\"WizardNewConfigurationDescription\">\n        <item name=\"android:text\">@string/wizard_new_conf_desc_step_5</item>\n    </style>\n\n    <style name=\"WizardNewConfigurationActionStep5\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:background\">@color/wizard_new_conf_accent_4</item>\n        <item name=\"android:textColor\">@color/wizard_text_button_on</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:textAllCaps\">true</item>\n        <item name=\"android:layout_marginTop\">@dimen/padding_large</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_xlarge</item>\n        <item name=\"android:paddingRight\">@dimen/padding_xlarge</item>\n        <item name=\"android:paddingTop\">@dimen/padding_default</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_default</item>\n        <item name=\"android:text\">@string/goTo9Cards</item>\n    </style>\n\n    <!-- Wizard Inline -->\n\n    <style name=\"WizardInlineContent\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:minHeight\">@dimen/min_height_actions_dialogs</item>\n        <item name=\"android:background\">@color/wizard_inline_background</item>\n    </style>\n\n    <style name=\"WizardInlineWorkspace\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">0px</item>\n        <item name=\"android:layout_weight\">1</item>\n    </style>\n\n    <style name=\"WizardInlineBottomPanel\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">@dimen/height_wizard_inline_bottom_panel</item>\n    </style>\n\n    <style name=\"WizardInlinePaginationPanel\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:orientation\">horizontal</item>\n    </style>\n\n    <style name=\"WizardInlineGotItAction\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:text\">@string/wizard_inline_got_it</item>\n        <item name=\"android:textColor\">@color/wizard_inline_title</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n        <item name=\"android:textAllCaps\">true</item>\n    </style>\n\n    <style name=\"WizardInlineSkipAction\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_gravity\">center_vertical</item>\n        <item name=\"android:padding\">@dimen/padding_large</item>\n        <item name=\"android:text\">@string/wizard_inline_skip</item>\n        <item name=\"android:textColor\">@color/wizard_inline_subtitle</item>\n        <item name=\"android:textSize\">@dimen/text_default</item>\n    </style>\n\n    <!-- Step Inline Item -->\n\n    <style name=\"WizardInlineItemRoot\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:gravity\">center_horizontal</item>\n    </style>\n\n    <style name=\"WizardInlineItemImage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingTop\">@dimen/padding_xlarge</item>\n    </style>\n\n    <style name=\"WizardInlineItemTitle\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:maxWidth\">@dimen/wizard_max_width_step_title</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:paddingTop\">@dimen/padding_xlarge</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_large</item>\n        <item name=\"android:paddingRight\">@dimen/padding_large</item>\n        <item name=\"android:textSize\">@dimen/text_xxxlarge</item>\n        <item name=\"android:textColor\">@color/wizard_inline_title</item>\n        <item name=\"android:lines\">2</item>\n        <item name=\"android:ellipsize\">end</item>\n    </style>\n\n    <style name=\"WizardInlineItemMessage\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingTop\">@dimen/padding_xlarge</item>\n        <item name=\"android:paddingLeft\">@dimen/padding_xlarge</item>\n        <item name=\"android:paddingRight\">@dimen/padding_xlarge</item>\n        <item name=\"android:paddingBottom\">@dimen/padding_large</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:textSize\">@dimen/text_xlarge</item>\n        <item name=\"android:textColor\">@color/wizard_inline_subtitle</item>\n        <item name=\"android:lineSpacingExtra\">@dimen/padding_small</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"AppThemeBase\" parent=\"AppThemeCommons\" />\n\n    <style name=\"AppTheme\" parent=\"AppThemeBase\" />\n\n    <style name=\"AppThemeWallpaperBase\" parent=\"AppThemeCommons\">\n        <!-- Show Wallpaper -->\n\n        <item name=\"android:windowShowWallpaper\">true</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:colorBackgroundCacheHint\">@null</item>\n\n    </style>\n\n    <style name=\"AppThemeWallpaper\" parent=\"AppThemeWallpaperBase\"/>\n\n    <style name=\"AppThemeDialog\" parent=\"Base.Theme.AppCompat.Dialog\">\n        <item name=\"android:fitsSystemWindows\">false</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:colorBackgroundCacheHint\">@null</item>\n    </style>\n\n    <style name=\"AppThemeCommons\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"windowActionBar\">false</item>\n\n        <item name=\"android:colorBackground\">@color/background_app</item>\n\n        <item name=\"colorPrimary\">@color/primary</item>\n        <item name=\"colorPrimaryDark\">@color/primary_dark</item>\n        <item name=\"colorAccent\">@color/accent</item>\n        <item name=\"colorControlHighlight\">@color/color_control_highlight</item>\n    </style>\n\n    <style name=\"AppThemePreferences\" parent=\"Theme.AppCompat.Light\">\n        <item name=\"android:windowNoTitle\">false</item>\n        <item name=\"android:windowActionBar\">true</item>\n        <item name=\"android:actionBarStyle\">@style/PreferencesActionBar</item>\n\n        <item name=\"windowNoTitle\">false</item>\n        <item name=\"windowActionBar\">true</item>\n        <item name=\"actionBarStyle\">@style/PreferencesActionBar</item>\n\n        <item name=\"colorPrimary\">@color/primary</item>\n        <item name=\"colorPrimaryDark\">@color/primary_dark</item>\n        <item name=\"colorAccent\">@color/accent</item>\n        <item name=\"colorControlHighlight\">@color/color_control_highlight</item>\n    </style>\n\n    <style name=\"AppThemeSharedContentBase\" parent=\"AppThemeCommons\">\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:colorBackgroundCacheHint\">@null</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowIsFloating\">true</item>\n        <item name=\"android:backgroundDimEnabled\">false</item>\n    </style>\n\n    <style name=\"AppThemeSharedContent\" parent=\"AppThemeSharedContentBase\"/>\n\n    <style name=\"AppCompatDialog\" parent=\"Theme.AppCompat.Light.Dialog.Alert\">\n        <item name=\"windowNoTitle\">true</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-es/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string-array name=\"days_letters\">\n        <item>D</item>\n        <item>L</item>\n        <item>M</item>\n        <item>M</item>\n        <item>J</item>\n        <item>V</item>\n        <item>S</item>\n    </string-array>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- General -->\n    <string name=\"edit\">Editar</string>\n    <string name=\"remove\">Eliminar</string>\n    <string name=\"moveTo\">Mover a</string>\n    <string name=\"appInfo\">Información</string>\n    <string name=\"uninstall\">Desinstalar</string>\n    <string name=\"make_public\">Hacer Pública</string>\n    <string name=\"share\">Compartir</string>\n    <string name=\"filter\">Filtrar</string>\n    <string name=\"loading\">Cargando</string>\n    <string name=\"next\">Siguiente</string>\n    <string name=\"contactUsError\">Ops! Por favor contacta con nosotros a través de 9cards@47deg.com si este problema persiste</string>\n    <string name=\"formFieldError\">¿Es posible que falte algo en el formulario?</string>\n    <string name=\"sendTo\">Enviar a</string>\n    <string name=\"buttonErrorReload\">Recargar</string>\n\n    <string name=\"resize\">Redimensionar</string>\n    <string name=\"move\">Mover</string>\n    <string name=\"delete\">Eliminar</string>\n\n    <string name=\"itemAddedToCollectionSuccessful\">Tarjeta añadida a %1$s</string>\n\n    <!-- Menu -->\n    <string name=\"collectionsTitle\">Colecciones</string>\n    <string name=\"momentsTitle\">Momentos</string>\n    <string name=\"profileTitle\">Perfil</string>\n    <string name=\"wallpaperTitle\">Wallpapers</string>\n    <string name=\"nineCardsSettingsTitle\">Ajustes</string>\n    <string name=\"widgetsTitle\">Widgets</string>\n\n    <!-- Wizard -->\n    <string name=\"buttonContinue\">Continuar</string>\n    <string name=\"buttonLetStart\">Comenzar</string>\n    <string name=\"buttonTryAgain\">Intentarlo de nuevo</string>\n    <string name=\"welcome\">\n        <![CDATA[\n            Un Launcher creado por y para<br/><b>Android Power Users</b>\n        ]]>\n    </string>\n\n    <string name=\"termsAndConditions\">\n        <![CDATA[\n            continuando, tu aceptas los<br/><u>Términos de Uso</u> y <u>Política de Privacidad</u>\n        ]]>\n    </string>\n\n    <string name=\"addDeviceTitle\">¡Hola, %1$s!</string>\n    <string name=\"addDeviceMessage\">Hemos detectado otros disposivos sincronizados con esta cuenta. ¿Quieres cargar las colecciones y temas de estos dispositivos?</string>\n\n    <string name=\"errorAccountsMessage\">Necesitas una cuenta de Google para sacar el máximo partido a 9Cards. Sólo tendremos acceso al nombre de la cuenta y se le informará y solicitará para futuros accesos</string>\n    <string name=\"errorFineLocationMessage\">Usámos tu localización para mostrar el tiempo en la pantalla de Momentos.</string>\n\n    <string name=\"canceledGooglePermission\">El permiso de Android Market es necesario para poder utilizar y sacar el máximo rendimiento a 9Cards</string>\n\n    <string name=\"errorLoginUser\">Error al acceder al servidor. Por favor inténtelo más tarde</string>\n    <string name=\"errorConnectingGoogle\">Error conectando con Google. Por favor inténtelo más tarde</string>\n    <string name=\"goTo9Cards\">¡Bienvenido en 9Cards!</string>\n    <string name=\"downloadingYourSetup\">Un momento, estamos descargando tu configuración</string>\n\n    <string name=\"downloadingBitmapsMessage\">Descargando iconos de tus apps</string>\n    <string name=\"savingConfigurationMessage\">Guardando la configuración en nuestros servidores</string>\n\n    <string name=\"wizard_loading_connecting_with_google\">Conectando con Google</string>\n    <string name=\"wizard_loading_request_google_permission\">Pidiendo permisos a Google</string>\n    <string name=\"wizard_loading_connecting_with_plus\">Conectando con Google Plus</string>\n    <string name=\"wizard_loading_devices\">Cargando dispositivos</string>\n    <string name=\"wizard_loading_looking_for_better_collection\">Creando las mejores colecciones con tus apps</string>\n    <string name=\"wizard_loading_saving_collections\">Guardando Colecciones</string>\n    <string name=\"wizard_loading_saving_moments\">Guardando Momentos</string>\n\n    <string name=\"creatingCollectionError\">Error creando Colecciones. Por favor inténtelo de nuevo</string>\n\n    <string name=\"loadUserConfigDeviceReplace\">Reemplazar %1$s con nueva configuración</string>\n    <string name=\"deviceNotFoundMessage\">Este dispositivo no está asociado a ninguna cuenta. Por favor, intentalo de nuevo</string>\n\n\n    <string name=\"wizard_step_title_1\">¡Bievenido otra vez!</string>\n    <string name=\"wizard_step_1\">9Cards te organiza todas tus aplicaciones en Colecciones inteligentes</string>\n    <string name=\"wizard_step_title_2\">Recuerda, 9Cards es social</string>\n    <string name=\"wizard_step_2\">Comparte Colecciones con tus amigos o descárgate Colecciones de la comunidad</string>\n    <string name=\"wizard_step_title_3\">Usa los momentos para adaptar 9Cards a tí</string>\n    <string name=\"wizard_step_3\">9Cards es un launcher inteligente que se adapta a tu día a día</string>\n    <string name=\"wizard_step_title_4\">Convierte tu teléfono más inteligente</string>\n    <string name=\"wizard_step_4\">Ajusta los Widgets que más utilices según el Momento</string>\n    <string name=\"wizard_step_title_5\">Gracias por usar 9Cards</string>\n    <string name=\"wizard_step_5\">Si quieres saber más sobre 9Cards, visita nuestro canal de YouTube</string>\n\n    <string name=\"errorAndroidMarketPermissionNotAccepted\">Tienes que aceptar el permiso de Android Market para poder usar 9Cards</string>\n    <string name=\"errorGooglePermissionNotAccepted\">Tienes que aceptar el permiso de Google para poder usar 9Cards</string>\n\n    <string name=\"errorNoCollectionsSelected\">Deberías seleccionar las colecciones que quieres usar</string>\n\n    <string name=\"currentDeviceTitle\">%s (dispositivo actual)</string>\n    <string name=\"newConfigurationSubtitle\">Crearemos una nueva configuración a partir de tus aplicaciones</string>\n    <string name=\"deviceMigratedFromV1\">Migrado de la versión anterior</string>\n    <string name=\"otherDevicesLink\">Configuraciones de otros dispositivos</string>\n\n    <string name=\"wizard_new_conf_steps_counter\">%1$s de 6</string>\n\n    <string name=\"wizard_new_conf_title_step_0\">Bienvenido a 9 Cards</string>\n    <string name=\"wizard_new_conf_desc_step_0\">Estamos buscando la mejor experiencia para ti en el launcher y nos guataría saber en que cosas estás interesado</string>\n\n    <string name=\"wizard_new_conf_title_step_1\">Aplicaciones agrupadas por categorías</string>\n    <string name=\"wizard_new_conf_desc_step_1\">\n        <![CDATA[\n            Hemos categorizados tus <b>%1$s apps</b> en <b>%2$s categorías</b> que nosotros llamamos <b>Colecciones</b>. ¿Qué categorías te gustaría usar?\n        ]]>\n    </string>\n\n    <string name=\"wizard_new_conf_collection_counter_step_1\">%1$s de %2$s colecciones</string>\n\n    <string name=\"wizard_new_conf_collection_name_step_1\">%1$s (%2$s apps)</string>\n    <string name=\"wizard_new_conf_collection_all_collections\">Todas las collecciones</string>\n\n    <string name=\"wizard_new_conf_title_step_2\">9Cards se adapta a tu día a día</string>\n    <string name=\"wizard_new_conf_desc_step_2\">\n        <![CDATA[\n            ¿Usa los mismos widgets en el trabajo, cuando escuchas música o por la noche? Ayúdanos a descubir como quieres usar 9Cards. Llamamos <b>Momentos</b> a cada Momento del día que tu haces cosas diferentes\n        ]]>\n    </string>\n\n    <string name=\"wizard_new_conf_title_step_3\">Momentos y Wifis que usas</string>\n    <string name=\"wizard_new_conf_description_step_3\">Selecciona los Momentos y el wifi para cada uno así podremos mostrarte la mejor experiencia en 9Cards</string>\n    <string name=\"wizard_new_conf_wifi_no_connected_step_3\">No hay wifi seleccionado</string>\n    <string name=\"wizard_new_conf_wifi_connected_step_3\">Conectado a \\\"%1$s\\\"</string>\n\n    <string name=\"wizard_new_conf_title_step_4\">¿En qué Momentos estás interesado?</string>\n    <string name=\"wizard_new_conf_description_step_4\">Selecciona los Momentos en los que estás interesado</string>\n\n    <string name=\"wizard_new_conf_title_step_5\">¡Gracias!</string>\n    <string name=\"wizard_new_conf_desc_step_5\">Recuerda que puedes create o configurar tus colecciones o Momentos in el launcher. Disfruta!</string>\n\n    <!-- Wizard Inline -->\n    <string name=\"wizard_inline_message_app_drawer\">Hola! Soy %1$s del equipo de 9Cards! Me encantaría contarte algo más sobre nuestro AppDrawer</string>\n    <string name=\"wizard_inline_message_collections\">Hola! Soy %1$s del equipo de 9Cards! Me gustaría explicarte como funcionan las Colecciones</string>\n    <string name=\"wizard_inline_message_profile\">Buenas! Soy %1$s del equipo de 9Cards! Déjame que te cuente que puedes hacer en tu Perfil</string>\n    <string name=\"wizard_inline_message_launcher\">Hola! Soy %1$s del equipo de 9Cards! Me gustaría contarte más sobre nuestro Launcher</string>\n    <string name=\"wizard_inline_show\">¡Vamos!</string>\n\n    <string name=\"wizard_inline_got_it\">¡Entendido!</string>\n    <string name=\"wizard_inline_skip\">Saltar</string>\n\n    <string name=\"wizard_inline_launcher_title_1\">Déjame que te enseñe como utilizar 9Cards con las Colecciones</string>\n    <string name=\"wizard_inline_launcher_1\">9Cards autocategoriza tus Apps en Colecciones inteligentes. Hemos creado las primeras Colecciones basadas en tus Apps. Echa un vistazo a otras Colecciones para sacar el máximo partido a 9Cards</string>\n\n    <string name=\"wizard_inline_launcher_title_2\">Juntos somos más inteligentes que en solitario</string>\n    <string name=\"wizard_inline_launcher_2\">Ahora, eres parte de la comunidad 9Cards, una gran familia de Power Users. Explora las diferentes Colecciones que otros usuarios han compartido contigo y descubre otras formas de utilizar tu móvil.</string>\n\n    <string name=\"wizard_inline_launcher_title_3\">Déjame que te enseñe como utilizar 9Cards con los Momentos</string>\n    <string name=\"wizard_inline_launcher_3\">Hemos llamado Momentos a las partes de tu día a día en la que haces algo en particular. Escuchar música, relajarte en tu casa, trabajar en tu oficina…</string>\n\n    <string name=\"wizard_inline_launcher_title_4\">Widgets adaptados a tu día a día</string>\n    <string name=\"wizard_inline_launcher_4\">Los Widgets son sin duda, una de las partes más productivas de Android, pero muchas veces también es una parte subestimada. Los Momentos permiten ordenar los Widgets y mostrarlos en el Momento del día en el que te encuentras</string>\n\n    <string name=\"wizard_inline_profile_title_1\">Tus Publicaciones</string>\n    <string name=\"wizard_inline_profile_1\">Echa un vistazo a tus Colecciones Públicas y al numero de descargas y subscriciones que tienen</string>\n\n    <string name=\"wizard_inline_profile_title_2\">Subscripciones</string>\n    <string name=\"wizard_inline_profile_2\">Cuando te descargas una Colección Pública, tienes la opción de estar subscrito a ella (actualizandose con las Apps que el usuario añada en cada momento) o utilizarla de la forma que mejor se adapte a tu día a día</string>\n\n    <string name=\"wizard_inline_profile_title_3\">Cuentas</string>\n    <string name=\"wizard_inline_profile_3\">Tienes más móviles y quieres utilizar 9Cards de la misma forma en todos ellos? No hay problema. Puedes sincronizar 9Cards en todos ellos!</string>\n\n    <string name=\"wizard_inline_appdrawer_title_1\">App Drawer</string>\n    <string name=\"wizard_inline_appdrawer_1\">Esta pantalla en particular es una de las que más nos gusta. Hemos trabajado duro para unificar en el App Drawer la información más importante de tu móvil (Apps y Contactos)</string>\n\n    <string name=\"wizard_inline_appdrawer_title_2\">Toda la información a un gesto de distancia</string>\n    <string name=\"wizard_inline_appdrawer_2\">Echa un vistazo a la forma en la que te mostramos la información, puedes acceder a tus Apps y Contactos con un fácil gesto y filtrarlo por Categorías y Recientemente Instalado o por Favoritos y Últimas Llamadas</string>\n\n    <string name=\"wizard_inline_appdrawer_title_3\">Lo hemos llamado Smart Scroll</string>\n    <string name=\"wizard_inline_appdrawer_3\">Accede más rápido a tus Apps y Contactos a través de nuestro Smart Scroll y recuerda que puedes arrastrar cualquier elemento a la Colección que más te guste</string>\n\n    <string name=\"wizard_inline_collection_title_1\">Decora tu colección para convertirla en única</string>\n    <string name=\"wizard_inline_collection_1\">Puedes adaptar tus colecciones para ser más productivo con las Apps, Contactos, Shortcuts y Recomendaciones que puedes encontrar dentro de 9Cards.</string>\n\n    <string name=\"wizard_inline_collection_title_2\">Swipe and Drop</string>\n    <string name=\"wizard_inline_collection_2\">Navegar entre tus Colecciones es pan comido con un fácil gesto horizontal. ¡Cambia, arrastra y coloca tus Apps y Colecciones en donde mejor\ty más productivo sea para tí!</string>\n\n    <string name=\"wizard_inline_collection_title_3\">Somos comunidad</string>\n    <string name=\"wizard_inline_collection_3\">¿Quieres compartir alguna de tus Colecciones con tu familia y amigos? ¿Quieres difundir la palabra de tu mejor Colección con la comunidad de 9Cards? ¡Haz tu Colección única y empieza a evangelizar!</string>\n\n    <!-- Moment / Widgets -->\n\n    <string name=\"select_moment\">Seleciona un Momento</string>\n\n    <string name=\"addMoment\">Añadir Momento</string>\n    <string name=\"editMoment\">Editar Momento</string>\n    <string name=\"editMomentWithName\">Editando %1$s</string>\n    <string name=\"changeMoment\">Cambiar Momento</string>\n\n    <string name=\"linkCollection\">Acceso Rápido a Colección</string>\n    <string name=\"hours\">Horas</string>\n    <string name=\"wifi\">Wifi</string>\n    <string name=\"bluetooth\">Bluetooth</string>\n\n    <string name=\"noLinkCollectionToMoment\">No vincular Colección a este Momento</string>\n\n    <string name=\"addHoursToEditMoment\">Añade restricciones de tiempo a este Momento y será mostrado en 9Cards sólo esas horas</string>\n\n    <string name=\"addWifiToEditMoment\">Añade tus Wifi a este Momento y lo mostraremos cuando estés conectado a uno de ellos</string>\n\n    <string name=\"addBluetoothToEditMoment\">Añade tus disposivos Bluetooth y lo mostraremos cuando estés conectado a uno de ellos</string>\n\n    <string name=\"linkCollectionMessage\">Puedes seleccionar una Colección y acceder rápidamente a las aplicaciones, contactos y accesos directos de esta Colección desde una barra a la derecha de 9Cards. Sólo tienes que hacer clic en el fondo o un swipe desde el borde derecho del Launcher</string>\n\n    <string name=\"widgetsErrorMessage\">Hemos tenido problemas cargando los widgets. Inténtalo más tarde</string>\n\n    <string name=\"wifiDisconnected\">No hemos encontrado redes WIFI. Tal vez sea porque no tienes conectada la Red WIFI de tu teléfono</string>\n\n    <string name=\"bluetoothDisconnected\">No hemos encontrado Bluetooth emparejados. Tal vez sea porque no tienes conectado el Bluetooth de tu teléfono</string>\n\n    <!-- Collections -->\n\n    <string name=\"createNewCollection\">Crear nueva Colección</string>\n    <string name=\"myCollections\">Mis Colecciones</string>\n    <string name=\"publicCollections\">Colecciones Públicas</string>\n\n    <string name=\"newCollection\">Nueva Colección</string>\n    <string name=\"editCollection\">Editar Colección</string>\n    <string name=\"collectionName\">Nombre de la Colección</string>\n    <string name=\"selectIcon\">Seleccionar icono</string>\n    <string name=\"selectColor\">Seleccionar color</string>\n\n    <string name=\"addMyCollection\">Añadir a mis Colecciones</string>\n    <string name=\"alreadyAddedCollection\">Colección ya añadida</string>\n\n    <string name=\"removeCollectionMessage\">¿Estás seguro de querer borrar esta Colección?</string>\n\n    <string name=\"minimumOneCollectionMessage\">No puedes eliminar la última Colección</string>\n\n    <string name=\"momentNotFoundMessage\">Momento no encontrado. Te recomendamos crear Colecciones desde la sección Mis Colecciones</string>\n    <string name=\"noPhoneCallPermissionMessage\">Se necesita el permiso para poder llamar al contacto</string>\n\n    <!-- Collections Detail -->\n\n    <string name=\"applications\">Aplicaciones</string>\n    <string name=\"recommendations\">Recomendaciones</string>\n    <string name=\"contacts\">Contactos</string>\n    <string name=\"shortcuts\">Accesos directos</string>\n\n    <string name=\"itemsSelected\">%s seleccionados</string>\n\n    <string name=\"unnamed\">Sin nombre</string>\n\n    <string name=\"removeCardMessage\">¿Estás seguro de querrer borrar esta tarjeta? (no desinstalará la aplicación)</string>\n\n    <string name=\"addApps\">Añadir Apps</string>\n    <string name=\"addRecommendations\">Añadir Recomendación</string>\n    <string name=\"addContact\">Añadir Contacto</string>\n    <string name=\"addShortcut\">Añadir Acceso Directo</string>\n\n    <!-- Publish Collections Wizard -->\n\n    <string name=\"alreadyPublishedCollection\">Colección ya publicada</string>\n\n    <string name=\"category\">Categoría</string>\n\n    <string name=\"publishCollectionHeader\">Publica tu Colección</string>\n    <string name=\"publishCollectionMessage\">Si publicas tu Colección, otros usuarios podrán encontrar nuevas aplicaciones</string>\n    <string name=\"addInformationHeader\">Añade información</string>\n    <string name=\"addInformationMessage\">Añade información sobre tu Colección para que otros usuarios puedan encontrarla</string>\n    <string name=\"publishAction\">Publicar</string>\n    <string name=\"publishingHeader\">Publicando</string>\n    <string name=\"publishingMessage\">Tu Colección se está publicando</string>\n    <string name=\"addInformationcollectionName\">Añade el nombre de la Colección</string>\n    <string name=\"addInformationCategory\">Selecciona una categoría</string>\n    <string name=\"congratulationsHeader\">¡Enhorabuena!</string>\n    <string name=\"congratulationsMessage\">Tu Colección se ha publicado con éxito</string>\n    <string name=\"shareCollection\">Comparte tu Colección</string>\n    <string name=\"publishCollectionError\">Oops! Una Colección sin aplicaciones no puede ser publicada</string>\n    <string name=\"notPublishedCollectionError\">Esta Colección debe ser publicada antes de ser compartida</string>\n    <string name=\"publishingError\">Oops! Ha ocurrido un error publicando tu Colección</string>\n    <string name=\"collectionError\">Oops! Ha ocurrido un error obteniendo esta Colección</string>\n\n    <!-- Apps Action -->\n\n    <string name=\"allApps\">Todas las aplicaciones</string>\n    <string name=\"appsByCategory\">Aplicaciones de %1$s</string>\n    <string name=\"selectedApps\">%s aplicaciones seleccionadas</string>\n\n    <!-- Contacts Action -->\n\n    <string name=\"allContacts\">Todos los contacts</string>\n    <string name=\"favoriteContacts\">Favoritos</string>\n\n    <string name=\"phones\">Teléfono</string>\n    <string name=\"sms\">SMS</string>\n    <string name=\"emails\">Email</string>\n\n    <string name=\"errorLoadingContacts\">Error cargando contactos</string>\n    <string name=\"errorLoadingCalls\">Error cargando las últimas llamadas</string>\n    <string name=\"errorContactsPermission\">Se requieren permisos de lectura sobre los contactos. ¿Quiere intentarlo de nuevo?</string>\n    <string name=\"errorCallsPermission\">Se requieren permisos de lectura sobre el registro de llamada. ¿Quiere intentarlo de nuevo?</string>\n    <string name=\"errorLoadingApps\">Error cargando aplicaciones</string>\n    <string name=\"errorLoadingShortcuts\">Error cargando accesos directos</string>\n    <string name=\"errorLoadingRecommendations\">Error cargando recomendaciones</string>\n\n    <string name=\"errorLoadingAddMoment\">Error cargando Momentos</string>\n    <string name=\"errorSavingAddMoment\">Error guardando el Momento</string>\n    <string name=\"emptyAddMoment\">Ya has añadido todos los Momentos a tu configuración</string>\n\n    <string name=\"errorLoadingPrivateCollections\">Error cargando tus Colecciones Privadas</string>\n    <string name=\"errorSavingPrivateCollections\">Error guardando tus Colección Privada</string>\n    <string name=\"emptyPrivateCollections\">9Cards crea Colecciones de tus aplicaciones instaladas y actualmente tienes todas las categorías que 9Cards te recomienda entre tus Colecciones. Pásate otro día por si tenemos una nueva Colección para ti</string>\n\n    <string name=\"errorLoadingPublicCollections\">Error cargando Colecciones Públicas</string>\n    <string name=\"errorSavingPublicCollections\">Error guardando Colección Pública</string>\n    <string name=\"emptyPublicCollections\">No hay Colecciones publicadas en esta categoría todavía… ¡Un Momento idoneo para crear una!</string>\n\n    <string name=\"sendEmailDialogChooserTitle\">Enviar correo electrónico</string>\n\n    <!-- Contacts Info -->\n\n    <string name=\"generalInfo\">Información General</string>\n\n    <string name=\"phoneHome\">Casa</string>\n    <string name=\"phoneMobile\">Móvil</string>\n    <string name=\"phoneWork\">Trabajo</string>\n    <string name=\"phoneMain\">Principal</string>\n    <string name=\"phoneFaxWork\">Fax del trabajo</string>\n    <string name=\"phoneFaxHome\">Fax de casa</string>\n    <string name=\"phonePager\">Busca</string>\n    <string name=\"phoneOther\">Otro</string>\n\n    <string name=\"emailHome\">Casa</string>\n    <string name=\"emailWork\">Trabajo</string>\n    <string name=\"emailOther\">Otro</string>\n\n    <!-- Recommendations Action -->\n\n    <string name=\"recommendationError\">Esta Colección no tiene aplicaciones todavía, por lo que, no podemos ayudarte con recomendaciones</string>\n\n    <string name=\"installNow\">Instalar ahora</string>\n    <string name=\"free\">Gratis</string>\n\n    <!-- Drawer -->\n    <string name=\"searchApps\">Buscar App…</string>\n    <string name=\"searchContacts\">Buscar Contacto…</string>\n\n    <string name=\"apps_alphabetical\">Alfabético</string>\n    <string name=\"apps_categories\">Por categorías</string>\n    <string name=\"apps_date\">Recientemente instalado</string>\n    <string name=\"contacts_alphabetical\">Alfabético</string>\n    <string name=\"contacts_favorites\">Favoritos</string>\n    <string name=\"contacts_last\">Últimas llamadas</string>\n    <string name=\"oneWeek\">1 semana</string>\n    <string name=\"twoWeeks\">2 semanas</string>\n    <string name=\"oneMonth\">1 mes</string>\n    <string name=\"twoMonths\">2 meses</string>\n    <string name=\"fourMonths\">4 meses</string>\n    <string name=\"sixMonths\">6 meses</string>\n    <string name=\"moreOfTwoMonths\">Más</string>\n\n    <string name=\"apps_not_found\">App no encontrada. Si lo deseas, puedes buscar en Google Play directamente, pulsando en el botón \"Buscar\" que aparece en el teclado</string>\n    <string name=\"contacts_not_found\">Contacto no encontrado en tu agenda para esta búsqueda</string>\n    <string name=\"searching_in_google_play\">Buscando en Google Play…</string>\n    <string name=\"apps_not_found_in_google_play\">App no encontrada en Google Play para esta búsqueda</string>\n\n    <!-- Categories -->\n\n    <string name=\"all_categories\">Todas</string>\n    <string name=\"all_apps\">Todas</string>\n    <string name=\"misc\">Misc</string>\n\n    <string name=\"apps\">Apps</string>\n    <string name=\"game\">Juegos</string>\n    <string name=\"books_and_reference\">Libros</string>\n    <string name=\"business\">Negocios</string>\n    <string name=\"comics\">Comics</string>\n    <string name=\"communication\">Comunicacion</string>\n    <string name=\"education\">Educación</string>\n    <string name=\"entertainment\">Ocio</string>\n    <string name=\"finance\">Finanzas</string>\n    <string name=\"health_and_fitness\">Salud</string>\n    <string name=\"libraries_and_demo\">Demos</string>\n    <string name=\"lifestyle\">Estilo de vida</string>\n    <string name=\"app_wallpaper\">Fondos Animados</string>\n    <string name=\"media_and_video\">Media</string>\n    <string name=\"medical\">Medicina</string>\n    <string name=\"music_and_audio\">Música</string>\n    <string name=\"news_and_magazines\">Noticias</string>\n    <string name=\"personalization\">Personalizar</string>\n    <string name=\"photography\">Fotografía</string>\n    <string name=\"productivity\">Productividad</string>\n    <string name=\"shopping\">Compras</string>\n    <string name=\"social\">Social</string>\n    <string name=\"sports\">Deporte</string>\n    <string name=\"tools\">Herramientas</string>\n    <string name=\"transportation\">Transporte</string>\n    <string name=\"travel_and_local\">Viajes</string>\n    <string name=\"weather\">Tiempo</string>\n    <string name=\"app_widgets\">Widgets</string>\n    <string name=\"video_players\">Reproductores</string>\n    <string name=\"maps_and_navigation\">Mapas</string>\n    <string name=\"art_and_design\">Diseño</string>\n    <string name=\"auto_and_vehicles\">Automoción</string>\n    <string name=\"beauty\">Belleza</string>\n    <string name=\"dating\">Citas</string>\n    <string name=\"events\">Eventos</string>\n    <string name=\"food_and_drink\">Comer y Beber</string>\n    <string name=\"house_and_home\">Casa</string>\n    <string name=\"parenting\">Ser Padres</string>\n    <string name=\"game_action\">Acción</string>\n    <string name=\"game_adventure\">Aventura</string>\n    <string name=\"game_arcade\">Arcade</string>\n    <string name=\"game_board\">Juegos de Mesa</string>\n    <string name=\"game_card\">Cartas</string>\n    <string name=\"game_casino\">Casino</string>\n    <string name=\"game_casual\">Casual</string>\n    <string name=\"game_educational\">Educativos</string>\n    <string name=\"game_family\">Cine Familiar</string>\n    <string name=\"game_wallpaper\">Fondos Animados de Juegos</string>\n    <string name=\"game_music\">Música</string>\n    <string name=\"game_puzzle\">Puzzle</string>\n    <string name=\"game_racing\">Carreras</string>\n    <string name=\"game_role_playing\">Juegos de Rol</string>\n    <string name=\"game_simulation\">Simulación</string>\n    <string name=\"game_sports\">Juegos de Deportes</string>\n    <string name=\"game_strategy\">Estrategía</string>\n    <string name=\"game_trivia\">Juegos de Preguntas y Respuestas</string>\n    <string name=\"game_widgets\">Juegos de Widgets</string>\n    <string name=\"game_word\">Palabras</string>\n\n    <!-- Moments -->\n    <string name=\"home\">Hogar</string>\n    <string name=\"homeDescription\">Apps &amp; Widgets para disfrutar y relajarse en casa</string>\n    <string name=\"work\">Trabajo</string>\n    <string name=\"workDescription\">Apps &amp; Widgets para trabajar y ser más productivo</string>\n    <string name=\"night\">Noche</string>\n    <string name=\"nightDescription\">Apps &amp; Widgets para ver películas, series o leer tus artículos favoritos</string>\n    <string name=\"study\">Estudio</string>\n    <string name=\"studyDescription\">Apps &amp; Widgets para aprovechar tu tiempo de estudio</string>\n    <string name=\"music\">Música</string>\n    <string name=\"musicDescription\">Apps &amp; Widgets para disfutar de tu música, podcast o radio</string>\n    <string name=\"car\">Coche</string>\n    <string name=\"carDescription\">Apps &amp; Widgets para saber el camino para llegar a casa</string>\n    <string name=\"sport\">Deporte</string>\n    <string name=\"sportDescription\">Apps &amp; Widgets para tu entrenamiento personal</string>\n    <string name=\"out_and_about\">Por ahí</string>\n    <string name=\"out_and_aboutDescription\">Apps &amp; Widgets para usar cuando estás en camino</string>\n\n    <!-- Profile -->\n    <string name=\"publications\">Publicaciones</string>\n    <string name=\"subscriptions\">Subscripciones</string>\n    <string name=\"subscriptions_number\">%s Subscripciones</string>\n    <string name=\"accounts\">Cuentas</string>\n\n    <string name=\"syncHeaderDevices\">Otros Dispositivos</string>\n    <string name=\"syncCurrent\">Dispositivo actual</string>\n    <string name=\"syncLastSynced\">Última sincronización: %s</string>\n\n    <string name=\"menuAccountSync\">Sincronizar</string>\n    <string name=\"menuAccountCopy\">Copiar</string>\n    <string name=\"menuAccountDelete\">Eliminar</string>\n    <string name=\"menuAccountChangeName\">Cambiar nombre</string>\n    <string name=\"menuAccountPrintInfo\">Imprimir información</string>\n\n    <string name=\"syncingAccount\">Iniciando la sincronización de tu cuenta</string>\n    <string name=\"accountSynced\">Cuenta sincronizada</string>\n\n    <string name=\"collectionAdded\">Esta Colección ha sido añadida a tus colecciones</string>\n\n    <string name=\"errorSyncing\">Error sincronizando tu cuenta. Por favor, inténtalo de nuevo</string>\n\n    <string name=\"errorLoadingUser\">Error cargando usuario. Por favor, inténtalo de nuevo</string>\n\n    <string name=\"errorEmptyNameForDevice\">Por favor, especifique un nombre para la nueva configuración</string>\n    <string name=\"errorEmptyNameForDeviceButton\">Por favor, inténtalo de nuevo</string>\n\n    <string name=\"logout\">Salir</string>\n\n    <string name=\"copyAccountSyncDialogTitle\">Nueva configuración</string>\n    <string name=\"renameAccountSyncDialogTitle\">Nuevo nombre</string>\n    <string name=\"hintNewConfigurationName\">Nombre</string>\n    <string name=\"removeAccountSyncMessage\">¿Estás seguro de querer borrar este dispositivo?</string>\n    <string name=\"emptyAccountsMessage\">\n        <![CDATA[\n            No hay ninguna <b>cuenta configurada</b> en este Momento. La sincronización está desactivada\n        ]]>\n    </string>\n\n    <string name=\"errorLoadingPublishedCollections\">\n        <![CDATA[\n            Ha habido un error cargando tus <b>Colecciones publicadas</b>\n        ]]>\n    </string>\n    <string name=\"emptyPublishedCollectionsMessage\">\n        <![CDATA[\n            No hay Colecciones <b>publicadas</b>. Quizás podrías publicar una de <b>tus Colecciones</b> :-)\n        ]]>\n    </string>\n\n    <string name=\"errorLoadingSubscriptions\">\n        <![CDATA[\n             Ha habido un error cargando tus <b>subscripciones</b>\n        ]]>\n    </string>\n    <string name=\"errorSubscribing\">Error suscribiendote a la Colección</string>\n    <string name=\"errorUnsubscribing\">Error desuscribiendote de la Colección</string>\n    <string name=\"emptySubscriptionsMessage\">\n        <![CDATA[\n            No estás <b>suscrito</b> a ninguna <b>Colección pública</b> en este Momento\n        ]]>\n    </string>\n\n    <string name=\"subscriptionActivated\">Suscripción activada</string>\n    <string name=\"subscriptionDeactivated\">Suscripción desactivada</string>\n\n    <!-- Preferences -->\n\n    <!-- Preferences Default Launcher -->\n    <string name=\"defaultLauncherTitle\">Lanzador por Defecto</string>\n    <string name=\"defaultLauncher\">Establecer como Lanzador por Defecto</string>\n\n    <!-- Preferences Personalization -->\n    <string name=\"personalizationPreferences\">Personalización</string>\n\n    <!-- Theme Preferences -->\n\n    <string name=\"lookFeelPrefTitle\">Aspecto General</string>\n    <string name=\"lookFeelPrefSummary\">Modifica el tema y otros aspectos generales</string>\n\n    <string name=\"selectThemeTitle\">Seleccionar Tema</string>\n    <string name=\"selectThemeSummary\">Resumen de Seleccionar Tema</string>\n\n    <string name=\"themesDark\">Oscuro</string>\n    <string name=\"themesLight\">Claro</string>\n\n    <string name=\"googleLogoTitle\">Logo de Google</string>\n    <string name=\"googleLogoSummary\">Tipo de Logo de Google en la barra de búsqueda</string>\n\n    <string name=\"colouredGoogleLogo\">Coloreado</string>\n    <string name=\"themeColourGoogleLogo\">Color del Tema</string>\n\n    <string name=\"fontsTitle\">Tamaño de Fuente</string>\n    <string name=\"fontsSummary\">Tamaño general de la fuente</string>\n\n    <string name=\"fontsSmall\">Pequeña</string>\n    <string name=\"fontsMedium\">Mediana</string>\n    <string name=\"fontsLarge\">Grande</string>\n\n    <string name=\"iconsTitle\">Tamaño de iconos</string>\n    <string name=\"iconsSummary\">Tamaño general de los iconos</string>\n\n    <string name=\"iconsSmall\">Pequeño</string>\n    <string name=\"iconsMedium\">Mediano</string>\n    <string name=\"iconsLarge\">Grande</string>\n\n    <string name=\"cardPaddingTitle\">Márgenes de Tarjetas</string>\n    <string name=\"cardPaddingSummary\">Márgenes entre tarjetas en las colecciones</string>\n\n    <string name=\"cardPaddingSmall\">Pequeño</string>\n    <string name=\"cardPaddingMedium\">Mediano</string>\n    <string name=\"cardPaddingLarge\">Grande</string>\n\n\n    <!-- Moment Preferences -->\n\n    <string name=\"momentsPrefTitle\">Momentos</string>\n    <string name=\"momentsPrefSummary\">Configura opciones para tus Momentos</string>\n\n    <string name=\"showMicSearchTitle\">Mostrar micro</string>\n    <string name=\"showMicSearchSummary\">Micro es mostrado en la barra del Momento</string>\n\n    <string name=\"showWeatherTitle\">Mostrar icono del tiempo</string>\n    <string name=\"showWeatherSummary\">Icono del tiempo es mostrado en la barra del Momento</string>\n\n    <!-- AppDrawer Preferences -->\n\n    <string name=\"appDrawerPrefTitle\">Cajón de Apps</string>\n    <string name=\"appDrawerPrefSummary\">Configura pantalla de aplicaciones y contactos en App Drawer</string>\n\n    <string name=\"appDrawerLongPressTitle\">Acción en Long-press</string>\n    <string name=\"appDrawerLongPressSummary\">Acción actual: %s</string>\n\n    <string name=\"appDrawerOpenKeyboard\">Abrir teclado</string>\n    <string name=\"appDrawerOpenContacts\">Abrir contactos</string>\n\n    <string name=\"appDrawerOpenAnimationTitle\">Animación</string>\n    <string name=\"appDrawerOpenAnimationSummary\">Actual animación: %s</string>\n\n    <string name=\"appDrawerOpenAnimationReveal\">Aparición en círculo</string>\n    <string name=\"appDrawerOpenAnimationFade\">Fade</string>\n\n    <string name=\"appDrawerFistTabContactTitle\">Contactos Favoritos al entrar</string>\n    <string name=\"appDrawerFistTabContactSummary\">Los Contacts Favorites aparecerán al entrar en el área de contactos</string>\n\n    <string name=\"appDrawerSelectItemsInScrollerTitle\">Resaltar elementos durante scroll</string>\n    <string name=\"appDrawerSelectItemsInScrollerSummary\">Los elementos seleccionados durante el scroll en la lista de aplicaciones y contactos</string>\n\n    <!-- Animations Preferences -->\n\n    <string name=\"animationsPrefTitle\">Animaciones</string>\n    <string name=\"animationsPrefSummary\">Elige las animaciones a usar en 9Cards</string>\n\n    <string name=\"speedTitle\">Velocidad de las Animaciones</string>\n    <string name=\"speedSummary\">Velocidad que quieres usar en todas las animaciones en 9Cards</string>\n\n    <string name=\"speedsSlow\">Lenta</string>\n    <string name=\"speedsNormal\">Normal</string>\n    <string name=\"speedsFast\">Rápida</string>\n\n    <string name=\"collectionOpeningTitle\">Abrir Colecciones</string>\n    <string name=\"collectionOpeningSummary\">Animación al abrir un Colección en 9Cards</string>\n\n    <string name=\"collectionOpeningsCircleReveal\">Círculo</string>\n    <string name=\"collectionOpeningsNoAnimation\">Sin Animación</string>\n\n    <string name=\"workspaceAnimationTitle\">Animación de Escritorio</string>\n    <string name=\"workspaceAnimationSummary\">Animación entre las pantallas del escritorio en la pantalla principal</string>\n\n    <string name=\"workspaceAnimationsHorizontalSlide\">Deslizamiento Horizontal</string>\n    <string name=\"workspaceAnimationsAppearsBehind\">Aparece desde atrás</string>\n\n    <string name=\"wallpaperAnimationTitle\">Movimiento Fondo de Pantalla</string>\n    <string name=\"wallpaperAnimationSummary\">La imagen de fondo se mueve en el desplazamiento entre pantallas</string>\n\n    <!-- Analytics Preferences -->\n\n    <string name=\"analyticsPrefTitle\">Analítica</string>\n    <string name=\"analyticsPrefSummary\">Aprende más sobre nuestra política de analítica</string>\n\n    <string name=\"analyticsEnabledPrefTitle\">Habilitar</string>\n    <string name=\"analyticsEnabledPrefSummary\">9 Cards usa Google Analytics para trackear el comportamiento de los usuarios. La información es anónima y es usada con el propósito de generar estadísticas de las apps más populares, mejores colecciones y mejor experiencia del usuario. Los datos son únicamente utilizados con este propósito y no serán vendidos, publicados ni usados de otra manera que para mejorar la experiencia del usuario. Si prefieres que tus datos anónimos no sean utilizados, puedes deshabilitarlo</string>\n\n    <!-- Developer Preferences -->\n\n    <string name=\"developerOptionsActivated\">Opciones para Desarrolladores Activada</string>\n\n    <string name=\"developerPrefTitle\">Opciones para Desarrolladores</string>\n    <string name=\"developerPrefSummary\">Sólo si eres un Android/Scala Developer :-)</string>\n\n    <string name=\"devAwarenessAPICategory\">Awareness API</string>\n\n    <string name=\"devProbablyActivityTitle\">Probable Actividad</string>\n    <string name=\"devHeadphonesTitle\">Auriculares</string>\n    <string name=\"devLocationTitle\">Localización</string>\n    <string name=\"devWeatherTitle\">Tiempo</string>\n\n    <string name=\"devLocalDatabaseCategory\">Base de Datos Local</string>\n\n    <string name=\"devAppsCategorizedTitle\">Aplicaciones Categorizadas</string>\n    <string name=\"devAppsCategorizedSummary\">%1$s de %2$s apps están categorizadas</string>\n\n    <string name=\"devAndroidTokenTitle\">Android Token</string>\n\n    <string name=\"devDeviceCloudIdTitle\">Device Cloud Id</string>\n\n    <string name=\"devDimensionsTitle\">Dimensión</string>\n\n    <string name=\"devCurrentDimenTitle\">Dimensión Actual</string>\n\n    <string name=\"devCurrentDensityTitle\">Densidad Actual</string>\n\n    <string name=\"devClickClipboard\">Click para copiar en portapapeles</string>\n    <string name=\"devCopiedToClipboard\">Copiado en portapapeles</string>\n\n    <string name=\"devWizardTitle\">Wizard</string>\n\n    <string name=\"devEmptyDevicesV1WizardTitle\">V1 Api no envía dispositivos</string>\n\n    <string name=\"devEmptyGoogleDriveDevicesWizardTitle\">Google Drive no envía dispositivos</string>\n\n    <string name=\"devOthersTitle\">Otros</string>\n\n    <string name=\"devClearCacheImagesTitle\">Limpiar imágenes en caché</string>\n\n    <string name=\"devShowPositionInCardsTitle\">Mostrar posiciones en las tarjetas</string>\n\n    <string name=\"devCacheCleared\">Caché limpiada</string>\n\n    <string name=\"devAppsListTitle\">Lista de Aplicaciones</string>\n\n    <string name=\"collectionDetailAddCardsMessage\">\n        <![CDATA[\n            Esta Colección no tiene tarjetas. Puedes añadir <b>aplicaciones, contactos, recomendaciones o atajos</b> desde el botón +\n        ]]>\n    </string>\n\n    <string name=\"devShowPrintInfoOptionInAccounts\">Imprimir información de Drive</string>\n\n    <string name=\"devIsStethoActiveFalse\">Activar Stetho</string>\n    <string name=\"devIsStethoActiveTrue\">Desactivar Stetho</string>\n    <string name=\"devIsStethoActiveSummary\">Requiere reiniciar la App</string>\n    <string name=\"devRestartApplication\">Reiniciar la App</string>\n    <string name=\"devBackendCategory\">Backend</string>\n    <string name=\"devOverrideBackendV2Url\">Sobrescribir URL del backend</string>\n    <string name=\"devOverrideBackendV2UrlSummary\">Requiere reiniciar la App</string>\n    <string name=\"devBackendV2Url\">URL del Backend</string>\n\n    <!-- Preferences About -->\n    <string name=\"others\">Otros</string>\n\n    <string name=\"aboutTitle\">Sobre 9Cards</string>\n    <string name=\"aboutSummary\">Conoce más sobre el producto y el equipo que esta detrás.</string>\n\n    <string name=\"sendFeedbackTitle\">Enviar Feedback</string>\n    <string name=\"sendFeedbackSummary\">Nos encantaría saber tu opinión sobre 9Cards</string>\n\n    <string name=\"about_header_open_source\">9Cards es un launcher Open Source diseñado por y para Power Android Users</string>\n\n    <string name=\"about_github\">9Cards en GitHub</string>\n\n    <string name=\"wizardInlineTitle\">Mostrar Wizards en línea</string>\n    <string name=\"wizardInlineSummary\">Mostraremos nuestros consejos de como utilizar 9Cards</string>\n    <string name=\"wizardInlineCleaned\">Tu verás los Wizard Inline de nuevo la pŕoxima vez</string>\n\n    <string name=\"rateGooglePlay\">Puntúa en Google Play</string>\n\n    <string name=\"team_name\">Hola, Soy %1$s</string>\n\n    <string name=\"knowTeam\">Conoce al Equipo</string>\n    <string name=\"libraries\">Librerías de Scala utilizadas</string>\n    <string name=\"about_47_deg\">Acerca de 47 Degrees</string>\n    <string name=\"followTwitter\">Síguenos en Twitter</string>\n    <string name=\"followFacebook\">Síguenos en Facebook</string>\n    <string name=\"followGooglePlus\">Síguenos en Google Plus</string>\n\n    <string name=\"only_client\">Sólo en Cliente</string>\n    <string name=\"only_server\">Sólo en Servidor</string>\n    <string name=\"server_and_client\">Servidor y Cliente</string>\n\n    <string name=\"openSource\">¡Somos de Código Abierto!</string>\n    <string name=\"openSourceSummary\">Conoce más sobre el Projecto de Código Abiertot</string>\n    <string name=\"web_47deg\">Sitio web de 47 Degrees</string>\n\n    <!-- Preferences Help -->\n    <string name=\"helpTitle\">Ayuda</string>\n    <string name=\"helpSummary\">¿Podemos ayudarte en algo?</string>\n\n    <!-- Shared Content -->\n    <string name=\"sharedIntentLabel\">Añadir tarjeta</string>\n    <string name=\"sharedContentDefaultTitle\">Página web</string>\n    <string name=\"sharedCardAdded\">Tarjeta añadida</string>\n    <string name=\"sharedContentErrorEmpty\">Sin contenido</string>\n    <string name=\"sharedContentErrorNotSupported\">Contenido compartido no soportado</string>\n    <string name=\"sharedContentErrorUnexpected\">Error inesperado</string>\n\n    <string name=\"sharedCollectionChangedNotificationTitle\">¡Colección actualizada!</string>\n    <string name=\"sharedCollectionChangedNotificationMsg\">La Colección \\'%s\\' ha sido actualizada</string>\n    <string name=\"sharedCollectionChangedNotificationUnsubscribe\">Desubscribirse</string>\n    <string name=\"sharedCollectionChangedNotificationSynchronize\">Sincronizar</string>\n    <string name=\"sharedCollectionUnsubscribed\">Desubscrito correctamente de la Colección</string>\n    <string name=\"sharedCollectionUpdated\">Colección correctamente actualizada</string>\n\n    <plurals name=\"sharedCollectionChangedNotificationBigMsg\">\n        <item quantity=\"one\">La Colección Pública \\'%s\\' tiene una nueva App. Pulsa aquí para añadirla a tu Colección.</item>\n        <item quantity=\"other\">La Colección Pública \\'%s\\' ha sido actualizada con nuevas Apps. Pulsa aquí para añadirlas a tu Colección.</item>\n    </plurals>\n\n    <!-- Edit Widgets -->\n    <string name=\"editingWidgets\">Editando widgets</string>\n    <string name=\"movingWidgets\">Moviendo widgets</string>\n    <string name=\"resizingWidgets\">Redimensionando widgets</string>\n    <string name=\"removeWidgetMessage\">¿Estás seguro de que quieres borrar este widget?</string>\n    <string name=\"noSpaceForWidget\">El widget no tiene espacio actualmente</string>\n    <string name=\"noMoveForWidget\">El widget no puede moverse ahí</string>\n    <string name=\"noResizeForWidget\">El widget no puede redimensionarse de esa forma</string>\n\n    <!-- Moments -->\n    <string name=\"addDuplicateItemError\">Elemento duplicado</string>\n    <string name=\"removeMomentMessage\">¿Estás seguro de querer borrar este Momento?</string>\n    <string name=\"cantRemoveOutAndAboutMoment\">No puedes eliminar este Momento</string>\n\n    <string name=\"message_moment_name\">%s tiene condiciones especiales</string>\n\n    <string name=\"specially_conditions_car\">El Momento Coche es activado automáticamente cuando 9Cards detecta que tu vas desplazándote en vehículo</string>\n    <string name=\"specially_conditions_music\">El Momento Música es activado automáticamente cuando conectas tus auriculares en tu móvil</string>\n    <string name=\"specially_conditions_out_and_about\">Este momento es activado automáticamente cuando no estas en un momento específico (por ejemplo: andando, de camino a casa, etc)</string>\n\n    <!-- App Links -->\n    <string name=\"loadingCollection\">Recuperando información de la Colección…</string>\n    <string name=\"linkNotSupportedError\">Enlace no soportado</string>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-sw400dp/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Size by density -->\n\n    <dimen name=\"text_xxxxlarge\">26sp</dimen>\n    <dimen name=\"text_xxxlarge\">22sp</dimen>\n    <dimen name=\"text_xxlarge\">20sp</dimen>\n    <dimen name=\"text_xlarge\">18sp</dimen>\n    <dimen name=\"text_large\">16sp</dimen>\n    <dimen name=\"text_default\">14sp</dimen>\n    <dimen name=\"text_medium\">12sp</dimen>\n    <dimen name=\"text_small\">10sp</dimen>\n    <dimen name=\"text_micro\">8sp</dimen>\n\n    <dimen name=\"padding_small\">4dp</dimen>\n    <dimen name=\"padding_default\">8dp</dimen>\n    <dimen name=\"padding_medium\">12dp</dimen>\n    <dimen name=\"padding_large\">16dp</dimen>\n    <dimen name=\"padding_xlarge\">24dp</dimen>\n    <dimen name=\"padding_xxlarge\">32dp</dimen>\n    <dimen name=\"padding_xxxlarge\">48dp</dimen>\n    <dimen name=\"padding_xxxxlarge\">64dp</dimen>\n\n    <dimen name=\"card_padding_small\">2dp</dimen>\n    <dimen name=\"card_padding_medium\">4dp</dimen>\n    <dimen name=\"card_padding_large\">8dp</dimen>\n\n    <dimen name=\"size_group_collection_small\">64dp</dimen>\n    <dimen name=\"size_group_collection_medium\">72dp</dimen>\n    <dimen name=\"size_group_collection_large\">80dp</dimen>\n\n    <dimen name=\"size_icon_app_small\">52dp</dimen>\n    <dimen name=\"size_icon_app_medium\">60dp</dimen>\n    <dimen name=\"size_icon_app_large\">68dp</dimen>\n\n    <dimen name=\"height_search_box\">48dp</dimen>\n\n    <dimen name=\"wizard_height_image_content\">360dp</dimen>\n    <dimen name=\"wizard_height_header_steps\">320dp</dimen>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-sw400dp/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Dimen -->\n    <string name=\"dimen_name\" translatable=\"false\">sw400dp</string>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-sw600dp/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Size by density -->\n\n    <dimen name=\"text_xxxxlarge\">26sp</dimen>\n    <dimen name=\"text_xxxlarge\">22sp</dimen>\n    <dimen name=\"text_xxlarge\">20sp</dimen>\n    <dimen name=\"text_xlarge\">18sp</dimen>\n    <dimen name=\"text_large\">16sp</dimen>\n    <dimen name=\"text_default\">14sp</dimen>\n    <dimen name=\"text_medium\">12sp</dimen>\n    <dimen name=\"text_small\">10sp</dimen>\n    <dimen name=\"text_micro\">8sp</dimen>\n\n    <dimen name=\"padding_small\">4dp</dimen>\n    <dimen name=\"padding_default\">8dp</dimen>\n    <dimen name=\"padding_medium\">12dp</dimen>\n    <dimen name=\"padding_large\">16dp</dimen>\n    <dimen name=\"padding_xlarge\">24dp</dimen>\n    <dimen name=\"padding_xxlarge\">32dp</dimen>\n    <dimen name=\"padding_xxxlarge\">48dp</dimen>\n    <dimen name=\"padding_xxxxlarge\">64dp</dimen>\n\n    <dimen name=\"card_padding_small\">2dp</dimen>\n    <dimen name=\"card_padding_medium\">4dp</dimen>\n    <dimen name=\"card_padding_large\">8dp</dimen>\n\n    <dimen name=\"size_group_collection_small\">64dp</dimen>\n    <dimen name=\"size_group_collection_medium\">72dp</dimen>\n    <dimen name=\"size_group_collection_large\">80dp</dimen>\n\n    <dimen name=\"size_icon_app_small\">52dp</dimen>\n    <dimen name=\"size_icon_app_medium\">60dp</dimen>\n    <dimen name=\"size_icon_app_large\">68dp</dimen>\n\n    <dimen name=\"height_search_box\">48dp</dimen>\n\n    <dimen name=\"wizard_height_image_content\">360dp</dimen>\n    <dimen name=\"wizard_height_header_steps\">320dp</dimen>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-sw600dp/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Dimen -->\n    <string name=\"dimen_name\" translatable=\"false\">sw600dp</string>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-v17/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"padding_checkbox\">8dp</dimen>\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-v19/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"AppThemeWallpaper\" parent=\"AppThemeWallpaperBase\">\n        <!-- Specifics for API 19 -->\n\n        <item name=\"android:fitsSystemWindows\">true</item>\n        <item name=\"android:windowTranslucentStatus\">true</item>\n        <item name=\"android:windowTranslucentNavigation\">true</item>\n\n    </style>\n\n    <style name=\"AppThemeSharedContent\" parent=\"AppThemeSharedContentBase\">\n        <!-- Specifics for API 19 -->\n\n        <item name=\"android:fitsSystemWindows\">true</item>\n        <item name=\"android:windowTranslucentStatus\">true</item>\n        <item name=\"android:windowTranslucentNavigation\">true</item>\n\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-v21/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"shadow_default\">#364550</color>\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-v21/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"margin_fab_button\">16dp</dimen>\n\n    <dimen name=\"margin_top_fab_button\">24dp</dimen>\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-v21/styles_actions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"BaseActionsToolbarDialog\" parent=\"BaseActionsToolbarDialogParent\">\n        <item name=\"android:elevation\">@dimen/elevation_toolbar</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-v21/styles_collections.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"CollectionsFabMenuContent\" parent=\"CollectionsFabMenuContentParent\">\n        <item name=\"android:elevation\">@dimen/elevation_fab_button</item>\n    </style>\n\n    <style name=\"CollectionsToolbar\" parent=\"CollectionsToolbarParent\">\n        <item name=\"android:elevation\">@dimen/elevation_default</item>\n    </style>\n\n    <style name=\"CollectionsIconContent\" parent=\"CollectionsIconContentParent\">\n        <item name=\"android:elevation\">@dimen/elevation_default</item>\n    </style>\n\n    <style name=\"CollectionsTabs\" parent=\"CollectionsTabsParent\">\n        <item name=\"android:elevation\">@dimen/elevation_default</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-v21/styles_fast_scroller.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"FastScrollerSignal\" parent=\"FastScrollerSignalParent\">\n        <item name=\"android:elevation\">@dimen/elevation_default</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-v21/styles_profile.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"ProfileAppBar\" parent=\"ProfileAppBarBase\">\n        <item name=\"android:elevation\">@dimen/elevation_toolbar</item>\n    </style>\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/values-v21/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"AppThemeWallpaper\" parent=\"AppThemeWallpaperBase\">\n        <!-- Specifics for API 21 -->\n        <item name=\"android:fitsSystemWindows\">true</item>\n\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n\n    </style>\n\n    <style name=\"AppThemeSharedContent\" parent=\"AppThemeSharedContentBase\">\n        <!-- Specifics for API 21 -->\n        <item name=\"android:fitsSystemWindows\">true</item>\n\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n\n    </style>\n\n</resources>"
  },
  {
    "path": "modules/app/src/main/res/xml/preferences_about.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <cards.nine.app.ui.components.preferences.AboutHeaderPreference\n        android:key=\"aboutHeader\"/>\n\n    <PreferenceCategory android:title=\"@string/knowTeam\">\n\n        <cards.nine.app.ui.components.preferences.TeamPreference\n            android:key=\"team\"/>\n\n    </PreferenceCategory>\n\n    <PreferenceCategory\n        android:key=\"libraries\"\n        android:title=\"@string/libraries\"/>\n\n    <PreferenceCategory android:title=\"@string/about_47_deg\">\n\n        <PreferenceScreen\n            android:title=\"@string/rateGooglePlay\">\n            <intent\n                android:action=\"android.intent.action.VIEW\"\n                android:data=\"@string/nine_cards_google_play\"/>\n        </PreferenceScreen>\n\n        <PreferenceScreen\n                android:title=\"@string/followTwitter\">\n            <intent\n                    android:action=\"android.intent.action.VIEW\"\n                    android:data=\"@string/follow_us_twitter\"/>\n        </PreferenceScreen>\n\n        <PreferenceScreen\n                android:title=\"@string/followFacebook\">\n            <intent\n                    android:action=\"android.intent.action.VIEW\"\n                    android:data=\"@string/follow_us_facebook\"/>\n        </PreferenceScreen>\n\n        <PreferenceScreen\n                android:title=\"@string/followGooglePlus\">\n            <intent\n                    android:action=\"android.intent.action.VIEW\"\n                    android:data=\"@string/follow_us_google_plus\"/>\n        </PreferenceScreen>\n\n        <PreferenceScreen\n            android:title=\"@string/openSource\"\n            android:summary=\"@string/openSourceSummary\">\n            <intent\n                android:action=\"android.intent.action.VIEW\"\n                android:data=\"@string/open_source\"/>\n        </PreferenceScreen>\n\n        <PreferenceScreen\n            android:title=\"@string/web_47deg\">\n            <intent\n                android:action=\"android.intent.action.VIEW\"\n                android:data=\"@string/web_47\"/>\n        </PreferenceScreen>\n\n    </PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "modules/app/src/main/res/xml/preferences_analytics.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <PreferenceCategory android:title=\"@string/analyticsPrefTitle\">\n\n        <CheckBoxPreference\n            android:key=\"analyticsEnabled\"\n            android:defaultValue=\"true\"\n            android:title=\"@string/analyticsEnabledPrefTitle\"/>\n\n        <Preference\n            android:summary=\"@string/analyticsEnabledPrefSummary\"/>\n\n    </PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "modules/app/src/main/res/xml/preferences_animations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <PreferenceCategory android:title=\"@string/animationsPrefTitle\">\n\n        <ListPreference\n            android:key=\"speed\"\n            android:defaultValue=\"1\"\n            android:entries=\"@array/speeds\"\n            android:entryValues=\"@array/speedsValues\"\n            android:title=\"@string/speedTitle\"\n            android:summary=\"@string/speedSummary\"/>\n\n        <ListPreference\n            android:key=\"collectionOpening\"\n            android:defaultValue=\"0\"\n            android:entries=\"@array/collectionOpenings\"\n            android:entryValues=\"@array/collectionOpeningsValues\"\n            android:title=\"@string/collectionOpeningTitle\"\n            android:summary=\"@string/collectionOpeningSummary\"/>\n\n        <ListPreference\n            android:key=\"workspaceAnimation\"\n            android:defaultValue=\"0\"\n            android:entries=\"@array/workspaceAnimations\"\n            android:entryValues=\"@array/workspaceAnimationsValues\"\n            android:title=\"@string/workspaceAnimationTitle\"\n            android:summary=\"@string/workspaceAnimationSummary\"/>\n\n        <CheckBoxPreference\n            android:key=\"wallpaperAnimation\"\n            android:defaultValue=\"true\"\n            android:title=\"@string/wallpaperAnimationTitle\"\n            android:summary=\"@string/wallpaperAnimationSummary\"/>\n\n    </PreferenceCategory>\n\n</PreferenceScreen>\n"
  },
  {
    "path": "modules/app/src/main/res/xml/preferences_app_drawer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <PreferenceCategory android:title=\"@string/appDrawerPrefTitle\">\n\n        <ListPreference\n            android:key=\"appDrawerLongPressAction\"\n            android:defaultValue=\"0\"\n            android:entries=\"@array/drawerActions\"\n            android:entryValues=\"@array/drawerActionsValues\"\n            android:title=\"@string/appDrawerLongPressTitle\"/>\n\n        <ListPreference\n            android:key=\"appDrawerAnimation\"\n            android:defaultValue=\"0\"\n            android:entries=\"@array/appDrawerOpenAnimations\"\n            android:entryValues=\"@array/appDrawerOpenAnimationsValues\"\n            android:title=\"@string/appDrawerOpenAnimationTitle\"/>\n\n        <CheckBoxPreference\n            android:key=\"appDrawerFavoriteContacts\"\n            android:defaultValue=\"false\"\n            android:title=\"@string/appDrawerFistTabContactTitle\"\n            android:summary=\"@string/appDrawerFistTabContactSummary\"/>\n\n        <CheckBoxPreference\n            android:key=\"appDrawerSelectItemsInScroller\"\n            android:defaultValue=\"true\"\n            android:title=\"@string/appDrawerSelectItemsInScrollerTitle\"\n            android:summary=\"@string/appDrawerSelectItemsInScrollerSummary\"/>\n\n    </PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "modules/app/src/main/res/xml/preferences_apps_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <PreferenceCategory\n        android:key=\"appsList\"\n        android:title=\"@string/devAppsListTitle\">\n\n    </PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "modules/app/src/main/res/xml/preferences_dev.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <PreferenceCategory\n        android:title=\"@string/devAppsCategorizedTitle\">\n\n        <Preference\n            android:key=\"appsCategorized\"\n            android:title=\"@string/devAppsCategorizedTitle\"/>\n\n        <Preference\n            android:key=\"androidToken\"\n            android:title=\"@string/devAndroidTokenTitle\"\n            android:summary=\"@string/devClickClipboard\"/>\n\n        <Preference\n            android:key=\"deviceCloudId\"\n            android:title=\"@string/devDeviceCloudIdTitle\"\n            android:summary=\"@string/devClickClipboard\"/>\n\n    </PreferenceCategory>\n\n    <PreferenceCategory\n        android:title=\"@string/devDimensionsTitle\">\n\n        <Preference\n            android:title=\"@string/devCurrentDimenTitle\"\n            android:summary=\"@string/dimen_name\"/>\n\n        <Preference\n            android:key=\"currentDensity\"\n            android:title=\"@string/devCurrentDensityTitle\"/>\n\n    </PreferenceCategory>\n\n    <PreferenceCategory\n        android:title=\"@string/devAwarenessAPICategory\">\n\n        <Preference\n            android:key=\"probablyActivity\"\n            android:title=\"@string/devProbablyActivityTitle\"/>\n\n        <Preference\n            android:key=\"headphones\"\n            android:title=\"@string/devHeadphonesTitle\"/>\n\n        <Preference\n            android:key=\"location\"\n            android:title=\"@string/devLocationTitle\"/>\n\n        <Preference\n            android:key=\"weather\"\n            android:title=\"@string/devWeatherTitle\"/>\n\n    </PreferenceCategory>\n\n    <PreferenceCategory\n        android:title=\"@string/devBackendCategory\">\n\n        <CheckBoxPreference\n            android:key=\"overrideBackendV2Url\"\n            android:title=\"@string/devOverrideBackendV2Url\"\n            android:summary=\"@string/devOverrideBackendV2UrlSummary\"/>\n\n        <EditTextPreference\n            android:key=\"backendV2Url\"\n            android:title=\"@string/devBackendV2Url\"/>\n\n    </PreferenceCategory>\n\n    <PreferenceCategory\n        android:title=\"@string/devWizardTitle\">\n\n        <CheckBoxPreference\n            android:key=\"v1EmptyDeviceWizard\"\n            android:defaultValue=\"false\"\n            android:title=\"@string/devEmptyDevicesV1WizardTitle\"/>\n\n        <CheckBoxPreference\n            android:key=\"googleDriveEmptyDeviceWizard\"\n            android:defaultValue=\"false\"\n            android:title=\"@string/devEmptyGoogleDriveDevicesWizardTitle\"/>\n\n    </PreferenceCategory>\n\n    <PreferenceCategory\n        android:title=\"@string/devOthersTitle\">\n\n        <Preference\n            android:key=\"restartApplication\"\n            android:title=\"@string/devRestartApplication\"/>\n\n        <Preference\n            android:key=\"clearCacheImages\"\n            android:title=\"@string/devClearCacheImagesTitle\"/>\n\n        <CheckBoxPreference\n            android:key=\"showPositionInCards\"\n            android:title=\"@string/devShowPositionInCardsTitle\"/>\n\n        <CheckBoxPreference\n            android:key=\"showPrintInfoOptionInAccounts\"\n            android:title=\"@string/devShowPrintInfoOptionInAccounts\"/>\n\n        <CheckBoxPreference\n            android:key=\"isStethoActive\"\n            android:title=\"@string/devIsStethoActiveFalse\"\n            android:summary=\"@string/devIsStethoActiveSummary\"/>\n\n        <CheckBoxPreference\n            android:key=\"isFlowUpActive\"\n            android:title=\"@string/devIsFlowUpActiveFalse\"\n            android:summary=\"@string/devIsFlowUpActiveSummary\"/>\n\n    </PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "modules/app/src/main/res/xml/preferences_devs_headers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <PreferenceCategory android:title=\"@string/personalizationPreferences\">\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_lookfeel\"\n            android:title=\"@string/lookFeelPrefTitle\"\n            android:key=\"lookFeelKey\"\n            android:summary=\"@string/lookFeelPrefSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_moments\"\n            android:title=\"@string/momentsPrefTitle\"\n            android:key=\"momentKey\"\n            android:summary=\"@string/momentsPrefSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_appdrawer\"\n            android:title=\"@string/appDrawerPrefTitle\"\n            android:key=\"appDrawerKey\"\n            android:summary=\"@string/appDrawerPrefSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_animations\"\n            android:title=\"@string/animationsPrefTitle\"\n            android:key=\"animationsKey\"\n            android:summary=\"@string/animationsPrefSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_analytics\"\n            android:title=\"@string/analyticsPrefTitle\"\n            android:key=\"analyticsKey\"\n            android:summary=\"@string/analyticsPrefSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_developers\"\n            android:title=\"@string/developerPrefTitle\"\n            android:key=\"developerKey\"\n            android:summary=\"@string/developerPrefSummary\"/>\n\n    </PreferenceCategory>\n\n    <PreferenceCategory android:title=\"@string/others\">\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_9cards\"\n            android:title=\"@string/wizardInlineTitle\"\n            android:key=\"wizardInlineKey\"\n            android:summary=\"@string/wizardInlineSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_about\"\n            android:title=\"@string/aboutTitle\"\n            android:key=\"aboutKey\"\n            android:summary=\"@string/aboutSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_feedback\"\n            android:title=\"@string/sendFeedbackTitle\"\n            android:key=\"feedbackKey\"\n            android:summary=\"@string/sendFeedbackSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_help\"\n            android:title=\"@string/helpTitle\"\n            android:key=\"helpKey\"\n            android:summary=\"@string/helpSummary\"/>\n\n    </PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "modules/app/src/main/res/xml/preferences_headers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <PreferenceCategory android:title=\"@string/personalizationPreferences\">\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_lookfeel\"\n            android:title=\"@string/lookFeelPrefTitle\"\n            android:key=\"lookFeelKey\"\n            android:summary=\"@string/lookFeelPrefSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_moments\"\n            android:title=\"@string/momentsPrefTitle\"\n            android:key=\"momentKey\"\n            android:summary=\"@string/momentsPrefSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_appdrawer\"\n            android:title=\"@string/appDrawerPrefTitle\"\n            android:key=\"appDrawerKey\"\n            android:summary=\"@string/appDrawerPrefSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_animations\"\n            android:title=\"@string/animationsPrefTitle\"\n            android:key=\"animationsKey\"\n            android:summary=\"@string/animationsPrefSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_analytics\"\n            android:title=\"@string/analyticsPrefTitle\"\n            android:key=\"analyticsKey\"\n            android:summary=\"@string/analyticsPrefSummary\"/>\n\n    </PreferenceCategory>\n\n    <PreferenceCategory android:title=\"@string/others\">\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_9cards\"\n            android:title=\"@string/wizardInlineTitle\"\n            android:key=\"wizardInlineKey\"\n            android:summary=\"@string/wizardInlineSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_about\"\n            android:title=\"@string/aboutTitle\"\n            android:key=\"aboutKey\"\n            android:summary=\"@string/aboutSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_feedback\"\n            android:title=\"@string/sendFeedbackTitle\"\n            android:key=\"feedbackKey\"\n            android:summary=\"@string/sendFeedbackSummary\"/>\n\n        <PreferenceScreen\n            android:icon=\"@drawable/icon_preferences_help\"\n            android:title=\"@string/helpTitle\"\n            android:key=\"helpKey\"\n            android:summary=\"@string/helpSummary\"/>\n\n    </PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "modules/app/src/main/res/xml/preferences_lookfeel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <PreferenceCategory android:title=\"@string/lookFeelPrefTitle\">\n\n        <ListPreference\n            android:key=\"theme\"\n            android:defaultValue=\"1\"\n            android:entries=\"@array/themes\"\n            android:entryValues=\"@array/themesValues\"\n            android:title=\"@string/selectThemeTitle\"\n            android:summary=\"@string/selectThemeSummary\"/>\n\n        <ListPreference\n            android:key=\"googleLogo\"\n            android:defaultValue=\"0\"\n            android:entries=\"@array/googleLogos\"\n            android:entryValues=\"@array/googleLogosValues\"\n            android:title=\"@string/googleLogoTitle\"\n            android:summary=\"@string/googleLogoSummary\"/>\n\n        <ListPreference\n            android:key=\"fontsSize\"\n            android:defaultValue=\"1\"\n            android:entries=\"@array/fonts\"\n            android:entryValues=\"@array/fontsValues\"\n            android:title=\"@string/fontsTitle\"\n            android:summary=\"@string/fontsSummary\"/>\n\n        <ListPreference\n            android:key=\"iconsSize\"\n            android:defaultValue=\"1\"\n            android:entries=\"@array/icons\"\n            android:entryValues=\"@array/iconsValues\"\n            android:title=\"@string/iconsTitle\"\n            android:summary=\"@string/iconsSummary\"/>\n\n        <ListPreference\n            android:key=\"cardPadding\"\n            android:defaultValue=\"1\"\n            android:entries=\"@array/cardPadding\"\n            android:entryValues=\"@array/cardPaddingValues\"\n            android:title=\"@string/cardPaddingTitle\"\n            android:summary=\"@string/cardPaddingSummary\"/>\n\n    </PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "modules/app/src/main/res/xml/preferences_moments.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <PreferenceCategory android:title=\"@string/momentsPrefTitle\">\n\n        <CheckBoxPreference\n            android:key=\"showMicSearchMoment\"\n            android:defaultValue=\"false\"\n            android:title=\"@string/showMicSearchTitle\"\n            android:summary=\"@string/showMicSearchSummary\"/>\n\n        <CheckBoxPreference\n            android:key=\"showWeatherMoment\"\n            android:defaultValue=\"true\"\n            android:title=\"@string/showWeatherTitle\"\n            android:summary=\"@string/showWeatherSummary\"/>\n\n    </PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/NineCardsApplication.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app\n\nimport android.support.multidex.MultiDexApplication\n\nclass NineCardsApplication extends MultiDexApplication\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/commons/BroadcastDispatcher.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.commons\n\nimport android.content._\nimport BroadcastDispatcher._\n\ntrait BroadcastDispatcher { dispatcher: ContextWrapper =>\n\n  val actionsFilters: Seq[String]\n\n  def manageCommand(action: String, command: Option[String]): Unit = {}\n\n  def manageQuestion(action: String): Unit = {}\n\n  lazy val broadcast = new BroadcastReceiver {\n    override def onReceive(context: Context, intent: Intent): Unit =\n      Option(intent) map { i =>\n        (Option(i.getAction),\n         Option(i.getStringExtra(keyType)),\n         Option(i.getStringExtra(keyCommand)))\n      } match {\n        case Some((Some(action: String), Some(key: String), data)) if key == commandType =>\n          manageCommand(action, data)\n        case Some((Some(action: String), Some(key: String), _)) if key == questionType =>\n          manageQuestion(action)\n        case _ =>\n      }\n  }\n\n  def registerDispatchers() = {\n    val intentFilter = new IntentFilter()\n    actionsFilters foreach intentFilter.addAction\n    registerReceiver(broadcast, intentFilter)\n  }\n\n  def unregisterDispatcher() = unregisterReceiver(broadcast)\n\n}\n\nobject BroadcastDispatcher {\n  val keyType      = \"broadcast-key-type\"\n  val questionType = \"broadcast-question\"\n  val commandType  = \"broadcast-command\"\n  val keyCommand   = \"broadcast-key-command\"\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/commons/ContextSupportPreferences.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.commons\n\nimport java.io.File\n\nimport android.content.{Context, SharedPreferences}\nimport cards.nine.commons.contexts.ContextSupport\nimport com.fortysevendeg.ninecardslauncher.R\n\ntrait ContextSupportPreferences { self: ContextSupport =>\n\n  override def getAppIconsDir: File =\n    context.getDir(getResources.getString(R.string.icons_apps_folder), Context.MODE_PRIVATE)\n\n  override def getSharedPreferences: SharedPreferences =\n    context.getSharedPreferences(\n      getResources.getString(R.string.shared_preferences_key),\n      Context.MODE_PRIVATE)\n\n  override def getActiveUserId: Option[Int] = {\n    val key  = getResources.getString(R.string.user_id_key)\n    val pref = getSharedPreferences\n    if (pref.contains(key)) {\n      Option(getSharedPreferences.getInt(key, 0))\n    } else {\n      None\n    }\n  }\n\n  override def setActiveUserId(id: Int): Unit = {\n    val editor = getSharedPreferences.edit()\n    editor.putInt(getResources.getString(R.string.user_id_key), id)\n    editor.apply()\n  }\n\n  override def addBluetoothDevice(device: String): Unit = {\n    import scala.collection.JavaConverters._\n    putBluetoothDevice((getBluetoothDevicesConnected + device).asJava)\n  }\n\n  override def removeBluetoothDevice(device: String): Unit = {\n    import scala.collection.JavaConverters._\n    putBluetoothDevice((getBluetoothDevicesConnected - device).asJava)\n  }\n\n  override def clearBluetoothDevices(): Unit = {\n    import scala.collection.JavaConverters._\n    putBluetoothDevice(Set.empty[String].asJava)\n  }\n\n  override def getBluetoothDevicesConnected: Set[String] = {\n    val key  = getResources.getString(R.string.bluetooth_key)\n    val pref = getSharedPreferences\n    if (pref.contains(key)) {\n      import scala.collection.JavaConversions._\n      getSharedPreferences.getStringSet(key, new java.util.TreeSet[String]()).toSet\n    } else {\n      Set.empty\n    }\n  }\n\n  private[this] def putBluetoothDevice(devices: java.util.Set[String]): Unit = {\n    val editor = getSharedPreferences.edit()\n    editor.putStringSet(getResources.getString(R.string.bluetooth_key), devices)\n    editor.apply()\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/commons/ContextSupportProvider.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.commons\n\nimport java.io.File\n\nimport android.accounts.AccountManager\nimport android.app.{Activity, AlarmManager, Application}\nimport android.content.pm.PackageManager\nimport android.content.res.{AssetManager, Resources}\nimport android.content.{ContentResolver, Context, Intent}\nimport cards.nine.commons.contexts.{ActivityContextSupport, ContextSupport}\nimport macroid.{ActivityContextWrapper, ContextWrapper}\n\nimport scala.ref.WeakReference\n\ntrait ContextSupportImpl extends ContextSupport {\n\n  override def getContentResolver: ContentResolver = context.getContentResolver\n\n  override def getPackageManager: PackageManager = context.getPackageManager\n\n  override def getResources: Resources = context.getResources\n\n  override def getFilesDir: File = context.getFilesDir\n\n  override def getAssets: AssetManager = context.getAssets\n\n  override def getPackageName: String = context.getPackageName\n\n  override def getAccountManager: AccountManager = AccountManager.get(context)\n\n  override def createIntent(classOf: Class[_]): Intent =\n    new Intent(context, classOf)\n\n  override def getAlarmManager: Option[AlarmManager] =\n    context.getSystemService(Context.ALARM_SERVICE) match {\n      case a: AlarmManager => Some(a)\n      case _               => None\n    }\n}\n\ntrait ContextSupportProvider {\n\n  implicit def contextSupport(implicit ctx: ContextWrapper): ContextSupport =\n    new ContextSupportImpl with ContextSupportPreferences {\n\n      override def application: Application =\n        ctx.application.asInstanceOf[Application]\n\n      override def context: Context = ctx.bestAvailable\n\n      override def getOriginal: WeakReference[Context] = ctx.original\n    }\n\n  implicit def activityContextSupport(\n      implicit ctx: ActivityContextWrapper): ActivityContextSupport =\n    new ContextSupportImpl with ActivityContextSupport with ContextSupportPreferences {\n\n      override def application: Application =\n        ctx.application.asInstanceOf[Application]\n\n      override def context: Context = ctx.bestAvailable\n\n      override def getActivity: Option[Activity] = ctx.original.get\n\n      override def getOriginal: WeakReference[Context] = ctx.original\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/commons/Conversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.commons\n\nimport android.content.Intent\nimport cards.nine.app.ui.commons.Constants._\nimport cards.nine.app.ui.commons.ops.WidgetsOps._\nimport cards.nine.models.types._\nimport cards.nine.models.{NotCategorizedPackage, SharedCollection, SharedCollectionPackage, _}\nimport macroid.ActivityContextWrapper\n\nimport scala.util.Random\n\ntrait Conversions extends AppNineCardsIntentConversions {\n\n  def toSeqCollectionData(collections: Seq[CloudStorageCollection]): Seq[CollectionData] =\n    collections.zipWithIndex.map(zipped => toCollectionData(zipped._1, zipped._2))\n\n  def toCollectionData(userCollection: CloudStorageCollection, position: Int): CollectionData =\n    CollectionData(\n      position = position,\n      name = userCollection.name,\n      collectionType = userCollection.collectionType,\n      icon = userCollection.icon,\n      themedColorIndex = position % numSpaces,\n      appsCategory = userCollection.category,\n      cards = toCardData(userCollection.items),\n      moment = userCollection.moment map toMoment,\n      originalSharedCollectionId = userCollection.originalSharedCollectionId,\n      sharedCollectionId = userCollection.sharedCollectionId,\n      sharedCollectionSubscribed = userCollection.sharedCollectionSubscribed getOrElse false)\n\n  def toCardData(items: Seq[CloudStorageCollectionItem]): Seq[CardData] =\n    items.zipWithIndex.map(zipped => toCardData(zipped._1, zipped._2))\n\n  def toCardData(item: CloudStorageCollectionItem, position: Int): CardData = {\n    val nineCardIntent = jsonToNineCardIntent(item.intent)\n    CardData(\n      position = position,\n      term = item.title,\n      packageName = nineCardIntent.extractPackageName(),\n      cardType = CardType(item.itemType),\n      intent = nineCardIntent)\n  }\n\n  def toCollectionDataFromSharedCollection(\n      collection: SharedCollection,\n      cards: Seq[CardData]): CollectionData =\n    CollectionData(\n      name = collection.name,\n      collectionType = AppsCollectionType,\n      icon = collection.icon,\n      themedColorIndex = Random.nextInt(numSpaces),\n      appsCategory = Option(collection.category),\n      cards = cards,\n      originalSharedCollectionId =\n        if (collection.publicCollectionStatus == PublishedByMe)\n          None\n        else Option(collection.sharedCollectionId),\n      sharedCollectionId = Option(collection.sharedCollectionId),\n      publicCollectionStatus = collection.publicCollectionStatus)\n\n  def toCardData(app: SharedCollectionPackage): CardData =\n    CardData(\n      term = app.title,\n      packageName = Option(app.packageName),\n      cardType = NoInstalledAppCardType,\n      intent = toNineCardIntent(app))\n\n  def toCardData(app: ApplicationData): CardData =\n    CardData(\n      term = app.name,\n      packageName = Option(app.packageName),\n      cardType = AppCardType,\n      intent = toNineCardIntent(app))\n\n  def toCardData(contact: Contact): CardData =\n    CardData(\n      term = contact.name,\n      packageName = None,\n      cardType = ContactCardType,\n      intent = contactToNineCardIntent(contact.lookupKey),\n      imagePath = Option(contact.photoUri))\n\n  def toCardData(app: NotCategorizedPackage): CardData =\n    CardData(\n      term = app.title,\n      packageName = Option(app.packageName),\n      cardType = AppCardType,\n      intent = toNineCardIntent(app))\n\n  def toCardData(dockAppData: DockAppData): Option[CardData] = {\n    dockAppData.dockType match {\n      case AppDockType =>\n        Option(\n          CardData(\n            term = dockAppData.name,\n            packageName = dockAppData.intent.extractPackageName(),\n            cardType = AppCardType,\n            intent = dockAppData.intent))\n      case ContactDockType =>\n        Option(\n          CardData(\n            term = dockAppData.name,\n            packageName = None,\n            cardType = ContactCardType,\n            intent = dockAppData.intent))\n      case _ => None\n    }\n  }\n\n  def toDockAppData(cloudStorageDockApp: CloudStorageDockApp): DockAppData =\n    DockAppData(\n      name = cloudStorageDockApp.name,\n      dockType = cloudStorageDockApp.dockType,\n      intent = jsonToNineCardIntent(cloudStorageDockApp.intent),\n      imagePath = cloudStorageDockApp.imagePath,\n      position = cloudStorageDockApp.position)\n\n}\n\ntrait AppNineCardsIntentConversions extends NineCardsIntentConversions {\n\n  def toNineCardIntent(app: SharedCollectionPackage): NineCardsIntent = {\n    val intent = NineCardsIntent(NineCardsIntentExtras(package_name = Option(app.packageName)))\n    intent.setAction(NineCardsIntentExtras.openNoInstalledApp)\n    intent\n  }\n\n  def toNineCardIntent(app: NotCategorizedPackage): NineCardsIntent = {\n    val intent = NineCardsIntent(NineCardsIntentExtras(package_name = Option(app.packageName)))\n    intent.setAction(NineCardsIntentExtras.openNoInstalledApp)\n    intent\n  }\n\n  def phoneToNineCardIntent(lookupKey: Option[String], tel: String): NineCardsIntent = {\n    val intent = NineCardsIntent(\n      NineCardsIntentExtras(contact_lookup_key = lookupKey, tel = Option(tel)))\n    intent.setAction(NineCardsIntentExtras.openPhone)\n    intent\n  }\n\n  def smsToNineCardIntent(lookupKey: Option[String], tel: String): NineCardsIntent = {\n    val intent = NineCardsIntent(\n      NineCardsIntentExtras(contact_lookup_key = lookupKey, tel = Option(tel)))\n    intent.setAction(NineCardsIntentExtras.openSms)\n    intent\n  }\n\n  def emailToNineCardIntent(lookupKey: Option[String], email: String): NineCardsIntent = {\n    val intent = NineCardsIntent(\n      NineCardsIntentExtras(contact_lookup_key = lookupKey, email = Option(email)))\n    intent.setAction(NineCardsIntentExtras.openEmail)\n    intent\n  }\n\n  def contactToNineCardIntent(lookupKey: String): NineCardsIntent = {\n    val intent = NineCardsIntent(NineCardsIntentExtras(contact_lookup_key = Option(lookupKey)))\n    intent.setAction(NineCardsIntentExtras.openContact)\n    intent\n  }\n\n  def toNineCardIntent(intent: Intent): NineCardsIntent = {\n    val i = NineCardsIntent(NineCardsIntentExtras())\n    i.fill(intent)\n    i\n  }\n\n  def toNineCardIntent(packageName: String, className: String): NineCardsIntent = {\n    val intent = NineCardsIntent(\n      NineCardsIntentExtras(package_name = Option(packageName), class_name = Option(className)))\n    intent.setAction(NineCardsIntentExtras.openApp)\n    intent.setClassName(packageName, className)\n    intent\n  }\n\n  def toMoment(cloudStorageMoment: CloudStorageMoment): MomentData =\n    MomentData(\n      collectionId = None,\n      timeslot = cloudStorageMoment.timeslot map toTimeSlot,\n      wifi = cloudStorageMoment.wifi,\n      bluetooth = cloudStorageMoment.bluetooth getOrElse Seq.empty,\n      headphone = cloudStorageMoment.headphones,\n      momentType = cloudStorageMoment.momentType,\n      widgets = cloudStorageMoment.widgets map toWidgetDataSeq)\n\n  def toMomentData(cloudStorageMoment: CloudStorageMoment): MomentData =\n    MomentData(\n      collectionId = None,\n      timeslot = cloudStorageMoment.timeslot map toTimeSlot,\n      wifi = cloudStorageMoment.wifi,\n      bluetooth = cloudStorageMoment.bluetooth getOrElse Seq.empty,\n      headphone = cloudStorageMoment.headphones,\n      momentType = cloudStorageMoment.momentType,\n      widgets = cloudStorageMoment.widgets map toWidgetDataSeq)\n\n  def toWidgetDataSeq(widgets: Seq[CloudStorageWidget]): Seq[WidgetData] =\n    widgets map toWidgetData\n\n  def toWidgetData(widget: CloudStorageWidget): WidgetData =\n    WidgetData(\n      packageName = widget.packageName,\n      className = widget.className,\n      appWidgetId = None,\n      area = WidgetArea(\n        startX = widget.area.startX,\n        startY = widget.area.startY,\n        spanX = widget.area.spanX,\n        spanY = widget.area.spanY),\n      widgetType = widget.widgetType,\n      label = widget.label,\n      imagePath = widget.imagePath,\n      intent = widget.intent map jsonToNineCardIntent)\n\n  def toWidgetData(widget: AppWidget, momentId: Int, maybeCell: Option[Cell] = None)(\n      implicit activityContextWrapper: ActivityContextWrapper): WidgetData = {\n    val cell = maybeCell getOrElse widget.getSimulateCell\n    WidgetData(\n      momentId = momentId,\n      packageName = widget.packageName,\n      className = widget.className,\n      appWidgetId = None,\n      area = WidgetArea(startX = 0, startY = 0, spanX = cell.spanX, spanY = cell.spanY),\n      widgetType = AppWidgetType,\n      label = None,\n      imagePath = None,\n      intent = None)\n  }\n\n  def toSeqWidgetData(moments: Seq[Moment], widgetsByMoment: Seq[WidgetsByMoment])(\n      implicit activityContextWrapper: ActivityContextWrapper): Seq[WidgetData] = moments flatMap {\n    moment =>\n      widgetsByMoment find (_.moment == moment.momentType) match {\n        case Some(widgetByMoment) =>\n          widgetByMoment.widgets.headOption map (toWidgetData(_, moment.id))\n        case _ => None\n      }\n  }\n\n  def toTimeSlot(cloudStorageMomentTimeSlot: CloudStorageMomentTimeSlot): MomentTimeSlot =\n    MomentTimeSlot(\n      from = cloudStorageMomentTimeSlot.from,\n      to = cloudStorageMomentTimeSlot.to,\n      days = cloudStorageMomentTimeSlot.days)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/di/Injector.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.di\n\nimport android.content.res.Resources\nimport cards.nine.api.rest.client.ServiceClient\nimport cards.nine.api.rest.client.http.OkHttpClient\nimport cards.nine.app.observers.ObserverRegister\nimport cards.nine.app.ui.preferences.commons.{\n  AnalyticsEnabled,\n  BackendV2Url,\n  IsStethoActive,\n  OverrideBackendV2Url\n}\nimport cards.nine.commons.contentresolver.{ContentResolverWrapperImpl, UriCreator}\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.models.types.NineCardsCategory\nimport cards.nine.models.types.NineCardsCategory._\nimport cards.nine.models.{CollectionProcessConfig, LauncherExecutorProcessConfig}\nimport cards.nine.process.accounts.UserAccountsProcess\nimport cards.nine.process.accounts.impl.UserAccountsProcessImpl\nimport cards.nine.process.cloud.CloudStorageProcess\nimport cards.nine.process.cloud.impl.CloudStorageProcessImpl\nimport cards.nine.process.collection.CollectionProcess\nimport cards.nine.process.collection.impl.CollectionProcessImpl\nimport cards.nine.process.device.DeviceProcess\nimport cards.nine.process.device.impl.DeviceProcessImpl\nimport cards.nine.process.intents.LauncherExecutorProcess\nimport cards.nine.process.intents.impl.LauncherExecutorProcessImpl\nimport cards.nine.process.moment.MomentProcess\nimport cards.nine.process.moment.impl.MomentProcessImpl\nimport cards.nine.process.recognition.RecognitionProcess\nimport cards.nine.process.recognition.impl.RecognitionProcessImpl\nimport cards.nine.process.recommendations.RecommendationsProcess\nimport cards.nine.process.recommendations.impl.RecommendationsProcessImpl\nimport cards.nine.process.sharedcollections.SharedCollectionsProcess\nimport cards.nine.process.sharedcollections.impl.SharedCollectionsProcessImpl\nimport cards.nine.process.social.SocialProfileProcess\nimport cards.nine.process.social.impl.SocialProfileProcessImpl\nimport cards.nine.process.theme.ThemeProcess\nimport cards.nine.process.theme.impl.ThemeProcessImpl\nimport cards.nine.process.thirdparty.ExternalServicesProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport cards.nine.process.trackevent.impl.TrackEventProcessImpl\nimport cards.nine.process.user.UserProcess\nimport cards.nine.process.user.impl.UserProcessImpl\nimport cards.nine.process.userv1.UserV1Process\nimport cards.nine.process.userv1.impl.UserV1ProcessImpl\nimport cards.nine.process.widget.WidgetProcess\nimport cards.nine.process.widget.impl.WidgetProcessImpl\nimport cards.nine.repository.repositories._\nimport cards.nine.services.analytics.impl.AnalyticsTrackServices\nimport cards.nine.services.api.impl.{ApiServicesConfig, ApiServicesImpl}\nimport cards.nine.services.apps.impl.AppsServicesImpl\nimport cards.nine.services.awareness.impl.GoogleAwarenessServicesImpl\nimport cards.nine.services.calls.impl.CallsServicesImpl\nimport cards.nine.services.contacts.impl.ContactsServicesImpl\nimport cards.nine.services.drive.impl.DriveServicesImpl\nimport cards.nine.services.image.impl.ImageServicesImpl\nimport cards.nine.services.intents.impl.LauncherIntentServicesImpl\nimport cards.nine.services.permissions.impl.AndroidSupportPermissionsServices\nimport cards.nine.services.persistence.impl.PersistenceServicesImpl\nimport cards.nine.services.plus.impl.GooglePlusServicesImpl\nimport cards.nine.services.shortcuts.impl.ShortcutsServicesImpl\nimport cards.nine.services.track.impl.{ConsoleTrackServices, DisableTrackServices}\nimport cards.nine.services.widgets.impl.WidgetsServicesImpl\nimport cards.nine.services.connectivity.impl.ConnectivityServicesImpl\nimport com.facebook.stetho.okhttp3.StethoInterceptor\nimport com.fortysevendeg.ninecardslauncher.R\nimport com.google.android.gms.analytics.GoogleAnalytics\nimport com.google.android.gms.awareness.Awareness\nimport com.google.android.gms.common.api.GoogleApiClient\n\ntrait Injector {\n\n  def resources: Resources\n\n  def recommendationsProcess: RecommendationsProcess\n\n  def deviceProcess: DeviceProcess\n\n  def collectionProcess: CollectionProcess\n\n  def momentProcess: MomentProcess\n\n  def userProcess: UserProcess\n\n  def userV1Process: UserV1Process\n\n  def widgetsProcess: WidgetProcess\n\n  def themeProcess: ThemeProcess\n\n  def sharedCollectionsProcess: SharedCollectionsProcess\n\n  def recognitionProcess: RecognitionProcess\n\n  def trackEventProcess: TrackEventProcess\n\n  def cloudStorageProcess: CloudStorageProcess\n\n  def socialProfileProcess: SocialProfileProcess\n\n  def observerRegister: ObserverRegister\n\n  def userAccountsProcess: UserAccountsProcess\n\n  def launcherExecutorProcess: LauncherExecutorProcess\n\n  def externalServicesProcess: ExternalServicesProcess\n\n}\n\nclass InjectorImpl(implicit contextSupport: ContextSupport) extends Injector {\n\n  private[this] def createHttpClient = {\n    val okHttpClientBuilder = new okhttp3.OkHttpClient.Builder()\n    if (IsStethoActive.readValueWith(contextSupport.context)) {\n      okHttpClientBuilder.addNetworkInterceptor(new StethoInterceptor)\n    }\n    new OkHttpClient(okHttpClientBuilder.build())\n  }\n\n  val resources = contextSupport.getResources\n\n  // Services\n\n  private[this] lazy val serviceHttpClient = createHttpClient\n\n  private[this] lazy val serviceClientV1 = new ServiceClient(\n    httpClient = serviceHttpClient,\n    baseUrl = resources.getString(R.string.api_base_url))\n\n  private[this] lazy val serviceClient = {\n    val backendV2Url =\n      if (OverrideBackendV2Url.readValueWith(contextSupport.context)) {\n        BackendV2Url.readValueWith(contextSupport.context)\n      } else resources.getString(R.string.api_v2_base_url)\n    new ServiceClient(httpClient = serviceHttpClient, baseUrl = backendV2Url)\n  }\n\n  private[this] lazy val apiServicesConfig = ApiServicesConfig(\n    appId = resources.getString(R.string.api_app_id),\n    appKey = resources.getString(R.string.api_app_key),\n    localization = resources.getString(R.string.api_localization))\n\n  private[this] lazy val apiServices = new ApiServicesImpl(\n    apiServicesConfig = apiServicesConfig,\n    apiService = new cards.nine.api.version2.ApiService(serviceClient),\n    apiServiceV1 = new cards.nine.api.version1.ApiService(serviceClientV1))\n\n  private[this] lazy val awarenessServices = {\n    val client = new GoogleApiClient.Builder(contextSupport.context).addApi(Awareness.API).build()\n    client.connect()\n    new GoogleAwarenessServicesImpl(client)\n  }\n\n  private[this] lazy val contentResolverWrapper =\n    new ContentResolverWrapperImpl(contextSupport.getContentResolver)\n\n  private[this] lazy val uriCreator = new UriCreator\n\n  private[this] lazy val persistenceServices =\n    new PersistenceServicesImpl(\n      appRepository = new AppRepository(contentResolverWrapper, uriCreator),\n      cardRepository = new CardRepository(contentResolverWrapper, uriCreator),\n      collectionRepository = new CollectionRepository(contentResolverWrapper, uriCreator),\n      dockAppRepository = new DockAppRepository(contentResolverWrapper, uriCreator),\n      momentRepository = new MomentRepository(contentResolverWrapper, uriCreator),\n      userRepository = new UserRepository(contentResolverWrapper, uriCreator),\n      widgetRepository = new WidgetRepository(contentResolverWrapper, uriCreator))\n\n  private[this] lazy val appsServices = new AppsServicesImpl()\n\n  private[this] lazy val shortcutsServices = new ShortcutsServicesImpl()\n\n  private[this] lazy val contactsServices = new ContactsServicesImpl(contentResolverWrapper)\n\n  private[this] lazy val imageServices = new ImageServicesImpl()\n\n  private[this] lazy val widgetsServices = new WidgetsServicesImpl()\n\n  private[this] lazy val callsServices = new CallsServicesImpl(contentResolverWrapper)\n\n  private[this] lazy val connectivityServices = new ConnectivityServicesImpl()\n\n  // Process\n\n  lazy val recommendationsProcess = new RecommendationsProcessImpl(\n    apiServices = apiServices,\n    persistenceServices = persistenceServices)\n\n  lazy val deviceProcess = new DeviceProcessImpl(\n    appsServices = appsServices,\n    apiServices = apiServices,\n    persistenceServices = persistenceServices,\n    shortcutsServices = shortcutsServices,\n    contactsServices = contactsServices,\n    imageServices = imageServices,\n    widgetsServices = widgetsServices,\n    callsServices = callsServices,\n    connectivityServices = connectivityServices)\n\n  private[this] lazy val nameCategories: Map[NineCardsCategory, String] =\n    (allCategories map { category =>\n      val identifier =\n        resources.getIdentifier(category.getIconResource, \"string\", contextSupport.getPackageName)\n      (category, if (identifier != 0) resources.getString(identifier) else category.name)\n    }).toMap\n\n  private[this] lazy val collectionProcessConfig = CollectionProcessConfig(\n    namesCategories = nameCategories)\n\n  lazy val collectionProcess = new CollectionProcessImpl(\n    collectionProcessConfig = collectionProcessConfig,\n    persistenceServices = persistenceServices,\n    contactsServices = contactsServices,\n    appsServices = appsServices,\n    apiServices = apiServices,\n    awarenessServices = awarenessServices,\n    widgetsServices = widgetsServices)\n\n  lazy val momentProcess = new MomentProcessImpl(\n    persistenceServices = persistenceServices,\n    connectivityServices = connectivityServices,\n    awarenessServices = awarenessServices)\n\n  lazy val userProcess =\n    new UserProcessImpl(apiServices = apiServices, persistenceServices = persistenceServices)\n\n  lazy val userV1Process = new UserV1ProcessImpl(\n    apiServices = apiServices,\n    persistenceServices = persistenceServices\n  )\n\n  lazy val widgetsProcess = new WidgetProcessImpl(persistenceServices = persistenceServices)\n\n  lazy val themeProcess = new ThemeProcessImpl()\n\n  lazy val sharedCollectionsProcess = new SharedCollectionsProcessImpl(\n    apiServices = apiServices,\n    persistenceServices = persistenceServices)\n\n  lazy val cloudStorageProcess =\n    new CloudStorageProcessImpl(new DriveServicesImpl, persistenceServices)\n\n  lazy val socialProfileProcess =\n    new SocialProfileProcessImpl(new GooglePlusServicesImpl, persistenceServices)\n\n  override def recognitionProcess: RecognitionProcess = {\n    val client = new GoogleApiClient.Builder(contextSupport.context).addApi(Awareness.API).build()\n    client.connect()\n    new RecognitionProcessImpl(persistenceServices, new GoogleAwarenessServicesImpl(client))\n  }\n\n  override def trackEventProcess: TrackEventProcess = {\n    def createService() = {\n      val resources = contextSupport.getResources\n      if (!AnalyticsEnabled.readValueWith(contextSupport.context)) {\n        new DisableTrackServices\n      } else if (resources.getString(R.string.analytics_enabled).equalsIgnoreCase(\"true\")) {\n        val track = GoogleAnalytics\n          .getInstance(contextSupport.context)\n          .newTracker(resources.getString(R.string.ga_trackingId))\n        track.setAppName(resources.getString(R.string.app_name))\n        track.enableAutoActivityTracking(false)\n        new AnalyticsTrackServices(track)\n      } else {\n        new ConsoleTrackServices\n      }\n    }\n    new TrackEventProcessImpl(createService())\n  }\n\n  lazy val observerRegister = new ObserverRegister(uriCreator)\n\n  lazy val userAccountsProcess: UserAccountsProcess = {\n    val services = new AndroidSupportPermissionsServices\n    new UserAccountsProcessImpl(services)\n  }\n\n  lazy val launcherExecutorProcess: LauncherExecutorProcess = {\n    val config = LauncherExecutorProcessConfig(\n      resources.getString(R.string.google_play_url),\n      resources.getString(R.string.sendEmailDialogChooserTitle),\n      resources.getString(R.string.sendTo))\n    val services = new LauncherIntentServicesImpl\n    new LauncherExecutorProcessImpl(config, services)\n  }\n\n  lazy val externalServicesProcess = new ExternalServicesProcess\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/observers/NineCardsObserver.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.observers\n\nimport android.app.AlarmManager\nimport android.content.Context\nimport android.database.ContentObserver\nimport android.net.Uri\nimport cards.nine.app.observers.NineCardsObserver._\nimport cards.nine.app.services.sync.SynchronizeDeviceService\nimport cards.nine.commons._\nimport cards.nine.commons.contentresolver.NotificationUri._\nimport cards.nine.commons.contexts.ContextSupport\n\nclass NineCardsObserver(implicit contextSupport: ContextSupport)\n    extends ContentObserver(javaNull) {\n\n  lazy val preferences =\n    contextSupport.context.getSharedPreferences(notificationPreferences, Context.MODE_PRIVATE)\n\n  private[this] def addCollectionId(collectionId: Int) = {\n    val collections = preferences.getString(collectionIdsKey, \"\")\n    val ids: Array[Int] =\n      collections.split(\",\").filterNot(_.isEmpty).map(_.toInt)\n    val newIds = ids.toSet + collectionId\n    preferences.edit.putString(collectionIdsKey, newIds.mkString(\",\")).apply()\n  }\n\n  private[this] val nextAlarmTime = 10 * 60 * 1000\n\n  override def onChange(selfChange: Boolean, uri: Uri): Unit =\n    matchUri(uri.toString) match {\n      case Some((UriCollection, maybeId)) =>\n        maybeId foreach addCollectionId\n        createAlarm()\n      case Some(_) => createAlarm()\n      case _       =>\n    }\n\n  private[this] def createAlarm(): Unit =\n    contextSupport.getAlarmManager foreach { alarmManager =>\n      val nextAlarm = System.currentTimeMillis() + nextAlarmTime\n      alarmManager.set(AlarmManager.RTC, nextAlarm, SynchronizeDeviceService.pendingIntent)\n    }\n}\n\nobject NineCardsObserver {\n\n  val notificationPreferences = \"notificationPreferences\"\n\n  val collectionIdsKey = \"_collectionsIds_\"\n\n  sealed trait UriType\n\n  case object UriCard       extends UriType\n  case object UriCollection extends UriType\n  case object UriDockApp    extends UriType\n  case object UriMoment     extends UriType\n  case object UriWidget     extends UriType\n\n  private[this] val uriRegExp =\n    s\"$baseUriNotificationString/([^/]+)(/(\\\\d+))?\".r\n\n  def matchUri(uri: String): Option[(UriType, Option[Int])] = {\n\n    def readType(uriType: String): Option[UriType] = uriType match {\n      case `appUriPath`        => Some(UriCard)\n      case `collectionUriPath` => Some(UriCollection)\n      case `dockAppUriPath`    => Some(UriDockApp)\n      case `momentUriPath`     => Some(UriMoment)\n      case `widgetUriPath`     => Some(UriWidget)\n      case _                   => None\n    }\n\n    uri match {\n      case uriRegExp(uriType, _, id) =>\n        readType(uriType) map ((_, Option(id) map (_.toInt)))\n      case _ => None\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/observers/ObserverRegister.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.observers\n\nimport cards.nine.app.ui.commons.{ImplicitsObserverExceptions, ObserverException}\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contentresolver.UriCreator\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.contentresolver.NotificationUri\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\n\nclass ObserverRegister(uriCreator: UriCreator)(implicit contextSupport: ContextSupport)\n    extends ImplicitsObserverExceptions {\n\n  import NotificationUri._\n\n  val baseUri = uriCreator.parse(baseUriNotificationString)\n\n  val observer = new NineCardsObserver\n\n  def registerObserverTask(): TaskService[Unit] = TaskService {\n    CatchAll[ObserverException] {\n      contextSupport.getContentResolver.registerContentObserver(baseUri, true, observer)\n    }\n  }\n\n  def unregisterObserverTask(): TaskService[Unit] = TaskService {\n    CatchAll[ObserverException] {\n      contextSupport.getContentResolver.unregisterContentObserver(observer)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/permissions/PermissionChecker.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.permissions\n\nimport android.content.pm.PackageManager\nimport android.support.v4.app.ActivityCompat\nimport android.support.v4.content.ContextCompat\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport macroid.ActivityContextWrapper\n\nclass PermissionChecker extends ImplicitsPermissionCheckerException {\n\n  import PermissionChecker._\n\n  private[this] def parsePermission(value: String): Option[AppPermission] =\n    value match {\n      case GetAccounts.value  => Some(GetAccounts)\n      case ReadContacts.value => Some(ReadContacts)\n      case ReadCallLog.value  => Some(ReadCallLog)\n      case CallPhone.value    => Some(CallPhone)\n      case _                  => None\n    }\n\n  def havePermission(permission: AppPermission)(\n      implicit contextWrapper: ActivityContextWrapper): Boolean =\n    ContextCompat.checkSelfPermission(contextWrapper.bestAvailable, permission.value) match {\n      case PackageManager.PERMISSION_GRANTED => true\n      case _                                 => false\n    }\n\n  def havePermissions(permissions: Seq[AppPermission])(\n      implicit contextWrapper: ActivityContextWrapper): Seq[PermissionResult] =\n    permissions map (permission => PermissionResult(permission, havePermission(permission)))\n\n  def shouldRequestPermission(permission: AppPermission)(\n      implicit contextWrapper: ActivityContextWrapper): Boolean =\n    contextWrapper.original.get exists { activity =>\n      ActivityCompat.shouldShowRequestPermissionRationale(activity, permission.value)\n    }\n\n  def shouldRequestPermissions(permissions: Seq[AppPermission])(\n      implicit contextWrapper: ActivityContextWrapper): Seq[PermissionResult] =\n    permissions map (permission => PermissionResult(permission, havePermission(permission)))\n\n  def requestPermissions(permissionRequestCode: Int, permissions: Seq[AppPermission])(\n      implicit contextWrapper: ActivityContextWrapper): Unit =\n    contextWrapper.original.get foreach { activity =>\n      ActivityCompat\n        .requestPermissions(activity, (permissions map (_.value)).toArray, permissionRequestCode)\n    }\n\n  def requestPermissionTask(permissionRequestCode: Int, permission: AppPermission)(\n      implicit contextWrapper: ActivityContextWrapper): TaskService[Unit] =\n    TaskService {\n      CatchAll[PermissionCheckerException] {\n        requestPermissions(permissionRequestCode, Array(permission))\n      }\n    }\n\n  def readPermissionRequestResultTask(\n      permissions: Array[String],\n      grantResults: Array[Int]): TaskService[Seq[PermissionResult]] =\n    TaskService {\n      CatchAll[PermissionCheckerException] {\n        (permissions zip grantResults) flatMap {\n          case (permission, grantResult) =>\n            parsePermission(permission) map (PermissionResult(\n              _,\n              grantResult == PackageManager.PERMISSION_GRANTED))\n        }\n      }\n    }\n\n}\n\nobject PermissionChecker {\n\n  sealed trait AppPermission {\n    val value: String\n  }\n\n  case object GetAccounts extends AppPermission {\n    override val value: String = android.Manifest.permission.GET_ACCOUNTS\n  }\n\n  case object ReadContacts extends AppPermission {\n    override val value: String = android.Manifest.permission.READ_CONTACTS\n  }\n\n  case object ReadCallLog extends AppPermission {\n    override val value: String = android.Manifest.permission.READ_CALL_LOG\n  }\n\n  case object CallPhone extends AppPermission {\n    override val value: String = android.Manifest.permission.CALL_PHONE\n  }\n\n  case class PermissionResult(permission: AppPermission, result: Boolean) {\n    def hasPermission(p: AppPermission): Boolean = permission == p && result\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/permissions/PermissionCheckerException.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.permissions\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class PermissionCheckerException(\n    message: String,\n    cause: Option[Throwable] = None,\n    recoverable: Boolean = false)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsPermissionCheckerException {\n  implicit def permissionCheckerExceptionConverter =\n    (t: Throwable) => PermissionCheckerException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/receivers/apps/AppBroadcastJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.receivers.apps\n\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.ui.commons.action_filters.{\n  AppInstalledActionFilter,\n  AppUninstalledActionFilter,\n  AppUpdatedActionFilter\n}\nimport cards.nine.app.ui.commons.{BroadAction, Jobs}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.Application.ApplicationDataOps\nimport cards.nine.models.{ApplicationData, Collection}\nimport cats.implicits._\nimport macroid.ContextWrapper\nimport monix.eval.Task\n\nclass AppBroadcastJobs(implicit contextWrapper: ContextWrapper) extends Jobs with Conversions {\n\n  def addApp(packageName: String): TaskService[Unit] = {\n\n    def insertAppInCollectionIfExist(maybeCollection: Option[Collection], app: ApplicationData) =\n      maybeCollection match {\n        case Some(collection) =>\n          di.collectionProcess.addCards(collection.id, Seq(app.toCardData))\n        case _ => TaskService(Task(Either.right((): Unit)))\n      }\n\n    for {\n      app        <- di.deviceProcess.saveApp(packageName)\n      collection <- di.collectionProcess.getCollectionByCategory(app.category)\n      _          <- insertAppInCollectionIfExist(collection, app)\n      _          <- di.collectionProcess.updateNoInstalledCardsInCollections(packageName)\n      _          <- sendBroadCastTask(BroadAction(AppInstalledActionFilter.action))\n    } yield (): Unit\n  }\n\n  def deleteApp(packageName: String): TaskService[Unit] =\n    for {\n      _ <- di.deviceProcess.deleteApp(packageName)\n      _ <- di.collectionProcess.deleteAllCardsByPackageName(packageName)\n      _ <- sendBroadCastTask(BroadAction(AppUninstalledActionFilter.action))\n    } yield (): Unit\n\n  def updateApp(packageName: String): TaskService[Unit] =\n    for {\n      _ <- di.deviceProcess.updateApp(packageName)\n      _ <- sendBroadCastTask(BroadAction(AppUpdatedActionFilter.action))\n    } yield (): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/receivers/apps/AppBroadcastReceiver.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.receivers.apps\n\nimport android.content.Intent._\nimport android.content.{BroadcastReceiver, Context, Intent}\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport macroid.ContextWrapper\n\nclass AppBroadcastReceiver extends BroadcastReceiver {\n\n  override def onReceive(context: Context, intent: Intent): Unit = {\n    val packageName = getPackageName(intent)\n    // We don't want to change 9Cards App\n    if (!context.getPackageName.equals(packageName)) {\n      val action    = intent.getAction\n      val replacing = intent.getBooleanExtra(EXTRA_REPLACING, false)\n\n      implicit val contextWrapper = ContextWrapper(context)\n\n      val jobs = new AppBroadcastJobs\n\n      (action, replacing) match {\n        case (ACTION_PACKAGE_ADDED, false) =>\n          jobs.addApp(packageName).resolveAsync()\n        case (ACTION_PACKAGE_REMOVED, false) =>\n          jobs.deleteApp(packageName).resolveAsync()\n        case (ACTION_PACKAGE_CHANGED | ACTION_PACKAGE_REPLACED, _) =>\n          jobs.updateApp(packageName).resolveAsync()\n        case (_, _) =>\n      }\n    }\n  }\n\n  private[this] def getPackageName(intent: Intent): String =\n    intent.getData.toString.replace(\"package:\", \"\")\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/receivers/bluetooth/BluetoothJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.receivers.bluetooth\n\nimport cards.nine.app.ui.commons.action_filters.MomentBestAvailableActionFilter\nimport cards.nine.app.ui.commons.{BroadAction, Jobs}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.commons.services.TaskService._\nimport macroid.ContextWrapper\n\nclass BluetoothJobs(implicit contextWrapper: ContextWrapper) extends Jobs {\n\n  def addBluetoothDevice(device: String): TaskService[Unit] =\n    for {\n      _ <- TaskService.right(contextSupport.addBluetoothDevice(device))\n      _ <- sendBroadCastTask(BroadAction(MomentBestAvailableActionFilter.action))\n    } yield ()\n\n  def removeBluetoothDevice(device: String): TaskService[Unit] =\n    for {\n      _ <- TaskService.right(contextSupport.removeBluetoothDevice(device))\n      _ <- sendBroadCastTask(BroadAction(MomentBestAvailableActionFilter.action))\n    } yield ()\n\n  def removeAllBluetoothDevices(): TaskService[Unit] =\n    for {\n      _ <- TaskService.right(contextSupport.clearBluetoothDevices())\n      _ <- sendBroadCastTask(BroadAction(MomentBestAvailableActionFilter.action))\n    } yield ()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/receivers/bluetooth/BluetoothReceiver.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.receivers.bluetooth\n\nimport android.bluetooth.{BluetoothAdapter, BluetoothDevice}\nimport android.content.{BroadcastReceiver, Context, Intent}\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport macroid.ContextWrapper\n\nclass BluetoothReceiver extends BroadcastReceiver {\n\n  override def onReceive(context: Context, intent: Intent): Unit = {\n\n    implicit val contextWrapper = ContextWrapper(context)\n\n    val jobs = new BluetoothJobs\n\n    (intent.getAction,\n     Option(intent.getParcelableExtra[BluetoothDevice](BluetoothDevice.EXTRA_DEVICE)),\n     Option(BluetoothAdapter.getDefaultAdapter)) match {\n      case (action, Some(device: BluetoothDevice), _)\n          if action.equals(BluetoothDevice.ACTION_ACL_CONNECTED) =>\n        jobs.addBluetoothDevice(device.getName).resolveAsync()\n      case (action, Some(device: BluetoothDevice), _)\n          if action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) =>\n        jobs.removeBluetoothDevice(device.getName).resolveAsync()\n      case (action, _, Some(adapter))\n          if action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) && adapter.getState == BluetoothAdapter.STATE_OFF =>\n        jobs.removeAllBluetoothDevices().resolveAsync()\n      case _ =>\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/receivers/moments/ConnectionStatusChangedJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.receivers.moments\n\nimport cards.nine.app.ui.commons.action_filters.MomentBestAvailableActionFilter\nimport cards.nine.app.ui.commons.{BroadAction, Jobs}\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.InVehicleFence\nimport macroid.ContextWrapper\n\nclass ConnectionStatusChangedJobs(implicit contextWrapper: ContextWrapper) extends Jobs {\n\n  def connectionStatusChanged(): TaskService[Unit] =\n    sendBroadCastTask(BroadAction(MomentBestAvailableActionFilter.action))\n\n  def headphoneStatusChanged(key: String): TaskService[Unit] =\n    sendBroadCastTask(BroadAction(MomentBestAvailableActionFilter.action, Option(key)))\n\n  def inVehicleStatusChanged(): TaskService[Unit] =\n    sendBroadCastTask(\n      BroadAction(MomentBestAvailableActionFilter.action, Option(InVehicleFence.key)))\n\n}\n\nobject ConnectionStatusChangedJobs {\n\n  val action = \"android.net.conn.CONNECTIVITY_CHANGE\"\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/receivers/moments/MomentBroadcastReceiver.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.receivers.moments\n\nimport android.content.{BroadcastReceiver, Context, Intent}\nimport android.net.ConnectivityManager\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.models.types.{HeadphonesFence, InVehicleFence}\nimport com.google.android.gms.awareness.fence.FenceState\nimport macroid.ContextWrapper\nimport monix.execution.cancelables.SerialCancelable\n\nimport scala.concurrent.duration._\nimport scala.util.Try\n\nclass MomentBroadcastReceiver extends BroadcastReceiver {\n\n  import MomentBroadcastReceiver._\n\n  override def onReceive(context: Context, intent: Intent): Unit = {\n\n    implicit val contextWrapper = ContextWrapper(context)\n\n    val connectionStatusChangedJobs = new ConnectionStatusChangedJobs\n\n    def verifyConnectionStatus(maybeNetworkInfo: Option[android.net.NetworkInfo]): Unit =\n      maybeNetworkInfo foreach { networkInfo =>\n        if (networkInfo.getType == ConnectivityManager.TYPE_WIFI) {\n          connectionStatusTaskRef := connectionStatusChangedJobs\n            .connectionStatusChanged()\n            .resolveAutoCancelableAsyncDelayed(5.seconds)\n        }\n      }\n\n    def verifyFenceStatus(maybeState: Option[FenceState]): Unit = {\n      import FenceState._\n      val maybeService = maybeState flatMap { state =>\n        Option(state.getFenceKey) collect {\n          case HeadphonesFence.keyIn if state.getCurrentState == TRUE =>\n            connectionStatusChangedJobs.headphoneStatusChanged(HeadphonesFence.keyIn)\n          case HeadphonesFence.keyOut if state.getCurrentState == TRUE =>\n            connectionStatusChangedJobs.headphoneStatusChanged(HeadphonesFence.keyOut)\n          case InVehicleFence.key\n              if state.getCurrentState == TRUE || state.getPreviousState == TRUE =>\n            connectionStatusChangedJobs.inVehicleStatusChanged()\n        }\n      }\n\n      maybeService foreach { service =>\n        fenceStatusRef := service.resolveAutoCancelableAsyncDelayed(500.millis)\n      }\n    }\n\n    Option(intent) foreach { i =>\n      Option(i.getAction) match {\n        case Some(ConnectionStatusChangedJobs.action) =>\n          verifyConnectionStatus(\n            Option(i.getParcelableExtra[android.net.NetworkInfo](\"networkInfo\")))\n        case Some(`momentFenceAction`) =>\n          verifyFenceStatus(Try(FenceState.extract(i)).toOption)\n        case _ =>\n      }\n    }\n\n  }\n\n}\n\nobject MomentBroadcastReceiver {\n\n  val connectionStatusTaskRef = SerialCancelable()\n\n  val fenceStatusRef = SerialCancelable()\n\n  val momentFenceAction = \"MOMENT_FENCE_ACTION\"\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/receivers/shortcuts/ShortcutBroadcastJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.receivers.shortcuts\n\nimport android.content.{Context, Intent}\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.ui.commons.action_filters.AppInstalledActionFilter\nimport cards.nine.app.ui.commons.{BroadAction, JobException, ShortcutJobs}\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.Card\nimport cats.implicits._\nimport macroid.ContextWrapper\n\nclass ShortcutBroadcastJobs(implicit contextWrapper: ContextWrapper)\n    extends ShortcutJobs\n    with Conversions {\n\n  import ShortcutBroadcastReceiver._\n\n  lazy val preferences =\n    contextSupport.context.getSharedPreferences(shortcutBroadcastPreferences, Context.MODE_PRIVATE)\n\n  def addShortcut(intent: Intent): TaskService[Unit] = {\n\n    def readCollectionId: TaskService[Option[Int]] = TaskService[Option[Int]] {\n      CatchAll[JobException] {\n        preferences.getInt(collectionIdKey, 0) match {\n          case n if n > 0 => Option(n)\n          case _          => None\n        }\n      }\n    }\n\n    def shortcutAdded(collectionId: Int, card: Card): TaskService[Unit] =\n      for {\n        _ <- di.trackEventProcess.addShortcutFromReceiver(card.term)\n        _ <- sendBroadCastTask(BroadAction(AppInstalledActionFilter.action))\n          .resolveLeftTo((): Unit)\n      } yield ()\n\n    def addShortcut(collectionId: Int): TaskService[Unit] =\n      for {\n        maybeCard <- addNewShortcut(collectionId, intent)\n        _ <- maybeCard match {\n          case Some(card) => shortcutAdded(collectionId, card)\n          case _          => TaskService.empty\n        }\n      } yield ()\n\n    for {\n      maybeId <- readCollectionId\n      _ <- maybeId match {\n        case Some(id) => addShortcut(id)\n        case _        => TaskService.empty\n      }\n    } yield ()\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/receivers/shortcuts/ShortcutBroadcastReceiver.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.receivers.shortcuts\n\nimport android.content.{BroadcastReceiver, Context, Intent}\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport macroid.ContextWrapper\n\nclass ShortcutBroadcastReceiver extends BroadcastReceiver {\n\n  import ShortcutBroadcastReceiver._\n\n  override def onReceive(context: Context, intent: Intent): Unit = {\n\n    implicit val contextWrapper = ContextWrapper(context)\n\n    val jobs = new ShortcutBroadcastJobs\n\n    Option(intent) foreach { i =>\n      if (i.getAction == actionInstallShortcut)\n        jobs.addShortcut(i).resolveAsync()\n    }\n  }\n\n}\n\nobject ShortcutBroadcastReceiver {\n\n  val actionInstallShortcut = \"com.android.launcher.action.INSTALL_SHORTCUT\"\n\n  val shortcutBroadcastPreferences = \"shortcutBroadcastPreferences\"\n\n  val collectionIdKey = \"_collectionId_\"\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/services/NineCardsFirebaseInstanceIdService.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.services\n\nimport android.app.Service\nimport cards.nine.app.commons.ContextSupportProvider\nimport com.google.firebase.iid.FirebaseInstanceIdService\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport macroid.Contexts\n\nclass NineCardsFirebaseInstanceIdService\n    extends FirebaseInstanceIdService\n    with Contexts[Service]\n    with ContextSupportProvider {\n\n  lazy val jobs = new NineCardsFirebaseJobs\n\n  override def onTokenRefresh(): Unit = {\n    super.onTokenRefresh()\n    jobs.updateDeviceToken().resolveAsync()\n  }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/services/NineCardsFirebaseJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.services\n\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.commons.services.TaskService._\nimport macroid.ContextWrapper\n\nclass NineCardsFirebaseJobs(implicit contextWrapper: ContextWrapper) extends Jobs {\n\n  def updateDeviceToken(): TaskService[Unit] =\n    for {\n      token <- di.externalServicesProcess.readFirebaseToken\n      _     <- di.userProcess.updateDeviceToken(token)\n    } yield ()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/services/NineCardsFirebaseMessagingService.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.services\n\nimport android.app.{Notification, NotificationManager, PendingIntent, Service}\nimport android.content.{Context, Intent}\nimport android.support.v4.app.NotificationCompat\nimport cards.nine.app.commons.ContextSupportProvider\nimport cards.nine.app.di.InjectorImpl\nimport cards.nine.app.services.payloads.SharedCollectionPayload\nimport cards.nine.app.services.sharedcollections.UpdateSharedCollectionService\nimport cards.nine.app.ui.commons.AppLog\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.models.types.PublishedByOther\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport com.google.firebase.messaging.{FirebaseMessagingService, RemoteMessage}\nimport macroid.Contexts\nimport play.api.libs.json._\n\nimport scala.util.{Failure, Success, Try}\n\nclass NineCardsFirebaseMessagingService\n    extends FirebaseMessagingService\n    with Contexts[Service]\n    with ContextSupportProvider {\n\n  import jsonImplicits._\n  import payloads._\n\n  implicit lazy val di = new InjectorImpl\n\n  lazy val builder = new NotificationCompat.Builder(this)\n\n  lazy val notifyManager = getSystemService(Context.NOTIFICATION_SERVICE)\n    .asInstanceOf[NotificationManager]\n\n  override def onMessageReceived(remoteMessage: RemoteMessage): Unit = {\n    super.onMessageReceived(remoteMessage)\n\n    def readJson[T <: Payload](json: String, f: (T) => Unit)(implicit reads: Reads[T]) =\n      Try(Json.parse(json)) match {\n        case Success(jsValue) => reads.reads(jsValue).asOpt foreach f\n        case Failure(ex) =>\n          AppLog.printErrorMessage(ex, Some(\"Error parsing message payload\"))\n      }\n\n    Option(remoteMessage.getData) foreach { data =>\n      (Option(data.get(\"payloadType\")), Option(data.get(\"payload\"))) match {\n        case (Some(`sharedCollectionPayload`), Some(json)) =>\n          readJson(json, sharedCollectionNotification)(sharedCollectionPayloadReads)\n        case _ =>\n      }\n    }\n\n  }\n\n  def sharedCollectionNotification(payload: SharedCollectionPayload): Unit = {\n    di.collectionProcess\n      .getCollectionBySharedCollectionId(payload.publicIdentifier)\n      .resolveAsync(\n        onResult = {\n          case None =>\n            di.sharedCollectionsProcess.unsubscribe(payload.publicIdentifier).resolveAsync()\n          case Some(col)\n              if col.publicCollectionStatus == PublishedByOther && !col.sharedCollectionSubscribed =>\n            di.sharedCollectionsProcess.unsubscribe(payload.publicIdentifier).resolveAsync()\n          case Some(col) if col.publicCollectionStatus == PublishedByOther =>\n            val collectionName = col.name\n            val collectionId   = col.id\n\n            val title =\n              resGetString(R.string.sharedCollectionChangedNotificationTitle)\n            val msg =\n              resGetString(R.string.sharedCollectionChangedNotificationMsg, collectionName)\n            val bigMsg = resGetQuantityString(\n              R.plurals.sharedCollectionChangedNotificationBigMsg,\n              payload.addedPackages.size,\n              collectionName)\n\n            val unsubscribeIntent =\n              new Intent(this, classOf[UpdateSharedCollectionService])\n            unsubscribeIntent.setAction(UpdateSharedCollectionService.actionUnsubscribe)\n            unsubscribeIntent\n              .putExtra(UpdateSharedCollectionService.intentExtraCollectionId, collectionId)\n            unsubscribeIntent.putExtra(\n              UpdateSharedCollectionService.intentExtraSharedCollectionId,\n              payload.publicIdentifier)\n\n            val syncIntent =\n              new Intent(this, classOf[UpdateSharedCollectionService])\n            syncIntent.setAction(UpdateSharedCollectionService.actionSync)\n            syncIntent\n              .putExtra(UpdateSharedCollectionService.intentExtraCollectionId, collectionId)\n            syncIntent.putExtra(\n              UpdateSharedCollectionService.intentExtraPackages,\n              payload.addedPackages.toArray[String])\n\n            val notification = builder\n              .setTicker(title)\n              .setContentTitle(title)\n              .setContentText(msg)\n              .setStyle(new NotificationCompat.BigTextStyle().bigText(bigMsg))\n              .setSmallIcon(R.drawable.icon_notification_default)\n              .setDefaults(Notification.DEFAULT_ALL)\n              .setContentIntent(PendingIntent.getService(this, 0, syncIntent, 0))\n              .addAction(\n                R.drawable.icon_notification_action_unsubscribe,\n                resGetString(R.string.sharedCollectionChangedNotificationUnsubscribe),\n                PendingIntent\n                  .getService(this, 0, unsubscribeIntent, PendingIntent.FLAG_UPDATE_CURRENT))\n              .addAction(\n                R.drawable.icon_notification_action_synchronize,\n                resGetString(R.string.sharedCollectionChangedNotificationSynchronize),\n                PendingIntent.getService(this, 0, syncIntent, PendingIntent.FLAG_UPDATE_CURRENT))\n              .build()\n\n            notifyManager.notify(UpdateSharedCollectionService.notificationId, notification)\n        },\n        onException = (_) => stopSelf()\n      )\n  }\n}\n\nobject jsonImplicits {\n\n  implicit val sharedCollectionPayloadReads =\n    Json.reads[SharedCollectionPayload]\n\n}\n\nobject payloads {\n\n  val sharedCollectionPayload = \"sharedCollection\"\n\n  sealed trait Payload {\n    def payloadType: String\n  }\n\n  case class SharedCollectionPayload(publicIdentifier: String, addedPackages: Seq[String])\n      extends Payload {\n\n    override def payloadType: String = sharedCollectionPayload\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/services/sharedcollections/UpdateSharedCollectionJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.services.sharedcollections\n\nimport android.content.Intent\nimport cards.nine.app.ui.commons.action_filters.AppInstalledActionFilter\nimport cards.nine.app.ui.commons.{BroadAction, ImplicitsJobExceptions, Jobs}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport macroid.ContextWrapper\n\nclass UpdateSharedCollectionJobs(actions: UpdateSharedCollectionUiActions)(\n    implicit contextWrapper: ContextWrapper)\n    extends Jobs\n    with ImplicitsJobExceptions {\n\n  import UpdateSharedCollectionService._\n\n  def handleIntent(intent: Intent): TaskService[Option[String]] = {\n\n    val (collectionId, sharedCollectionId, action, packages) = Option(intent) match {\n      case Some(i) =>\n        (readIntValue(i, intentExtraCollectionId),\n         readStringValue(i, intentExtraSharedCollectionId),\n         Option(i.getAction),\n         readArrayValue(i, intentExtraPackages).getOrElse(Array.empty))\n      case _ => (None, None, None, Array.empty[String])\n    }\n\n    (collectionId, sharedCollectionId, action) match {\n      case (_, Some(shareCollectionId), Some(`actionUnsubscribe`)) =>\n        for {\n          _ <- actions.cancelNotification()\n          _ <- di.sharedCollectionsProcess.unsubscribe(shareCollectionId)\n          _ <- actions.showUnsubscribedMessage\n        } yield Some(actionUnsubscribe)\n      case (Some(id), _, Some(`actionSync`)) =>\n        for {\n          _ <- actions.cancelNotification()\n          _ <- di.collectionProcess.addPackages(id, packages.toSeq)\n          _ <- sendBroadCastTask(BroadAction(AppInstalledActionFilter.action))\n          _ <- actions.showCollectionUpdatedMessage\n        } yield Some(actionSync)\n      case _ => TaskService.right(None)\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/services/sharedcollections/UpdateSharedCollectionService.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.services.sharedcollections\n\nimport android.app.{IntentService, Service}\nimport android.content.Intent\nimport cards.nine.app.commons.ContextSupportProvider\nimport cards.nine.app.ui.commons.AppLog._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport macroid.Contexts\n\nclass UpdateSharedCollectionService\n    extends IntentService(\"updateSharedCollectionService\")\n    with Contexts[Service]\n    with ContextSupportProvider\n    with UpdateSharedCollectionUiActions {\n\n  lazy val jobs = new UpdateSharedCollectionJobs(this)\n\n  override def onHandleIntent(intent: Intent): Unit =\n    jobs.handleIntent(intent).resolveAsync(onException = e => printErrorMessage(e))\n\n}\n\nobject UpdateSharedCollectionService {\n\n  val intentExtraCollectionId = \"_collectionId_\"\n\n  val intentExtraSharedCollectionId = \"_sharedCollectionId_\"\n\n  val intentExtraPackages = \"_addedPackages_\"\n\n  val actionUnsubscribe = \"unsubscribeCollection\"\n\n  val actionSync = \"syncCollection\"\n\n  val notificationId: Int = 2101\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/services/sharedcollections/UpdateSharedCollectionUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.services.sharedcollections\n\nimport android.app.{NotificationManager, Service}\nimport android.content.Context\nimport macroid.extras.UIActionsExtras._\nimport cards.nine.app.ui.commons.{ImplicitsUiExceptions, UiException}\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.Contexts\n\ntrait UpdateSharedCollectionUiActions extends ImplicitsUiExceptions { self: Contexts[Service] =>\n\n  lazy val notifyManager = serviceContextWrapper.bestAvailable\n    .getSystemService(Context.NOTIFICATION_SERVICE)\n    .asInstanceOf[NotificationManager]\n\n  def cancelNotification(): TaskService[Unit] = TaskService {\n    CatchAll[UiException](notifyManager.cancel(UpdateSharedCollectionService.notificationId))\n  }\n\n  def showUnsubscribedMessage: TaskService[Unit] =\n    uiShortToast(R.string.sharedCollectionUnsubscribed).toService()\n\n  def showCollectionUpdatedMessage: TaskService[Unit] =\n    uiShortToast(R.string.sharedCollectionUpdated).toService()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/services/sync/SynchronizeDeviceService.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.services.sync\n\nimport android.app.{IntentService, PendingIntent, Service}\nimport android.content.Intent\nimport cards.nine.app.commons.{BroadcastDispatcher, ContextSupportProvider}\nimport cards.nine.app.services.sync.SynchronizeDeviceService._\nimport cards.nine.app.ui.commons.{AppLog, SynchronizeDeviceJobs}\nimport cards.nine.app.ui.commons.SyncDeviceState._\nimport cards.nine.app.ui.commons.action_filters._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.process.cloud.CloudStorageClientListener\nimport cards.nine.process.sharedcollections.SharedCollectionsConfigurationException\nimport com.google.android.gms.common.ConnectionResult\nimport com.google.android.gms.common.api.GoogleApiClient\nimport macroid.Contexts\n\nclass SynchronizeDeviceService\n    extends IntentService(\"synchronizeDeviceService\")\n    with Contexts[Service]\n    with ContextSupportProvider\n    with BroadcastDispatcher\n    with CloudStorageClientListener {\n\n  lazy val serviceJobs = new SynchronizeDeviceServiceJobs\n\n  lazy val syncJobs = new SynchronizeDeviceJobs\n\n  val actionsFilters: Seq[String] = SyncActionFilter.cases map (_.action)\n\n  override def manageQuestion(action: String): Unit =\n    SyncActionFilter(action) match {\n      case SyncAskActionFilter => serviceJobs.sendActualAnswer().resolveAsync()\n      case _                   =>\n    }\n\n  override def onHandleIntent(intent: Intent): Unit = {\n    registerDispatchers()\n\n    statuses = statuses.reset()\n\n    (for {\n      _ <- TaskService.right(statuses = statuses.copy(currentState = Option(stateSyncing)))\n      _ <- serviceJobs.sendActualState()\n      _ <- serviceJobs.synchronizeCollections()\n      _ <- serviceJobs.connectGoogleApiClient()\n    } yield ()).resolveAsyncService(onException = onException)\n  }\n\n  override def onDestroy(): Unit = {\n    super.onDestroy()\n    statuses = statuses.reset()\n    unregisterDispatcher()\n  }\n\n  override def onDriveConnectionSuspended(cause: Int): Unit = {}\n\n  override def onDriveConnected(): Unit = {\n    (statuses.apiClient match {\n      case Some(apiClient) =>\n        for {\n          _ <- serviceJobs.cancelAlarm().resolveLeftTo((): Unit)\n          _ <- syncJobs.synchronizeDevice(apiClient)\n          _ <- TaskService.right(statuses = statuses.copy(currentState = Some(stateSuccess)))\n          _ <- finalizeService(error = false)\n        } yield ()\n      case None => serviceJobs.connectGoogleApiClient()\n    }).resolveAsyncService(onException = onException)\n  }\n\n  override def onDriveConnectionFailed(connectionResult: ConnectionResult): Unit =\n    finalizeService(error = true).resolveAsync()\n\n  private[this] def finalizeService(error: Boolean): TaskService[Unit] = {\n    val state = if (error) stateFailure else stateSuccess\n    for {\n      _ <- TaskService.right(statuses = statuses.copy(currentState = Some(state)))\n      _ <- serviceJobs.sendActualState()\n      _ <- serviceJobs.finalizeService(this)\n    } yield ()\n  }\n\n  private[this] def onException: (Throwable) => TaskService[Unit] = {\n    case e: SharedCollectionsConfigurationException =>\n      AppLog.invalidConfigurationV2\n      finalizeService(error = true)\n    case _ => finalizeService(error = true)\n  }\n}\n\nobject SynchronizeDeviceService {\n\n  val requestCode = 5001\n\n  var statuses = SynchronizeDeviceServiceStatuses()\n\n  def pendingIntent(implicit contextSupport: ContextSupport): PendingIntent =\n    PendingIntent.getService(\n      contextSupport.context,\n      requestCode,\n      new Intent(contextSupport.context, classOf[SynchronizeDeviceService]),\n      PendingIntent.FLAG_CANCEL_CURRENT)\n\n}\n\ncase class SynchronizeDeviceServiceStatuses(\n    currentState: Option[String] = None,\n    apiClient: Option[GoogleApiClient] = None) {\n\n  def reset() = SynchronizeDeviceServiceStatuses()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/services/sync/SynchronizeDeviceServiceJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.services.sync\n\nimport android.app.Service\nimport android.content.Context\nimport cards.nine.app.observers.NineCardsObserver._\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.commons.action_filters.{SyncAnswerActionFilter, SyncStateActionFilter}\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.{AppCardType, PublishedByMe}\nimport cards.nine.models.{Collection, User}\nimport macroid.ContextWrapper\nimport monix.eval.Task\n\nclass SynchronizeDeviceServiceJobs(implicit contextWrapper: ContextWrapper)\n    extends SynchronizeDeviceJobs\n    with ImplicitsJobExceptions {\n\n  import SynchronizeDeviceService._\n\n  lazy val preferences =\n    contextSupport.context.getSharedPreferences(notificationPreferences, Context.MODE_PRIVATE)\n\n  def synchronizeCollections(): TaskService[Unit] = {\n\n    def updateCollections() = {\n\n      def updateCollection(collectionId: Int) = {\n\n        def updateSharedCollection(collection: Collection): TaskService[Option[String]] =\n          (collection.publicCollectionStatus, collection.sharedCollectionId) match {\n            case (PublishedByMe, Some(sharedCollectionId)) =>\n              di.sharedCollectionsProcess\n                .updateSharedCollection(\n                  sharedCollectionId = sharedCollectionId,\n                  name = collection.name,\n                  packages =\n                    collection.cards.filter(_.cardType == AppCardType).flatMap(_.packageName))\n                .map(Option(_))\n            case _ => TaskService.right(None)\n          }\n\n        for {\n          collection <- di.collectionProcess\n            .getCollectionById(collectionId)\n            .resolveOption(s\"Can't find the collection with id $collectionId\")\n          _ <- updateSharedCollection(collection)\n        } yield ()\n      }\n\n      val ids            = preferences.getString(collectionIdsKey, \"\").split(\",\").toSeq\n      val updateServices = ids filterNot (_.isEmpty) map (id => updateCollection(id.toInt).value)\n      preferences.edit().remove(collectionIdsKey).apply()\n\n      TaskService {\n        Task.gatherUnordered(updateServices) map (_ => Right((): Unit))\n      }\n\n    }\n\n    for {\n      _ <- di.deviceProcess.synchronizeInstalledApps\n      _ <- updateCollections()\n    } yield ()\n  }\n\n  def cancelAlarm(): TaskService[Unit] = TaskService {\n    CatchAll[JobException] {\n      contextSupport.getAlarmManager foreach (_.cancel(SynchronizeDeviceService.pendingIntent))\n    }\n  }\n\n  def sendActualAnswer(): TaskService[Unit] =\n    sendBroadCastTask(BroadAction(SyncAnswerActionFilter.action, statuses.currentState))\n\n  def sendActualState(): TaskService[Unit] =\n    sendBroadCastTask(BroadAction(SyncStateActionFilter.action, statuses.currentState))\n\n  def connectGoogleApiClient(): TaskService[Unit] = {\n\n    def connectUser(user: User): TaskService[Unit] =\n      user.email match {\n        case Some(email) =>\n          for {\n            apiClient <- di.cloudStorageProcess.createCloudStorageClient(email)\n            _         <- TaskService.right(statuses = statuses.copy(apiClient = Some(apiClient)))\n            _         <- TaskService(CatchAll[JobException](apiClient.connect()))\n          } yield ()\n        case None =>\n          TaskService.left(JobException(\"User without email, can't sync\"))\n      }\n\n    for {\n      user   <- di.userProcess.getUser\n      client <- connectUser(user)\n    } yield ()\n  }\n\n  def finalizeService(service: Service): TaskService[Unit] = {\n\n    def secure(f: => Unit): TaskService[Unit] =\n      TaskService(CatchAll[JobException](f)).resolveLeftTo((): Unit)\n\n    for {\n      _ <- statuses.apiClient map (client =>\n                                     secure(client.disconnect())) getOrElse TaskService.empty\n      _ <- secure(service.stopForeground(true))\n      _ <- secure(service.stopSelf())\n    } yield ()\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/applinks/AppLinksReceiverActivity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.applinks\n\nimport android.app.Activity\nimport android.os.Bundle\nimport android.support.v7.app.AppCompatActivity\nimport cards.nine.app.commons.ContextSupportProvider\nimport cards.nine.app.ui.commons.{ActivityUiContext, AppLog, UiContext}\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.process.sharedcollections.SharedCollectionsConfigurationException\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.Contexts\n\nclass AppLinksReceiverActivity\n    extends AppCompatActivity\n    with Contexts[AppCompatActivity]\n    with ContextSupportProvider\n    with TypedFindView {\n\n  implicit lazy val uiContext: UiContext[Activity] = ActivityUiContext(this)\n\n  lazy val actions = new AppLinksReceiverUiActions(AppLinksReceiverDOM(this))\n\n  lazy val jobs = new AppLinksReceiverJobs(actions)\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n\n    setContentView(R.layout.app_link_dialog_activity)\n\n    Option(getIntent) match {\n      case Some(intent) =>\n        jobs\n          .uriReceived(intent.getData)\n          .resolveAsync(onException = (e: Throwable) =>\n            e match {\n              case e: SharedCollectionsConfigurationException =>\n                AppLog.invalidConfigurationV2\n                finish()\n              case _ =>\n                finish()\n          })\n      case None => finish()\n    }\n  }\n}\n\ncase class AppLinksReceiverDOM(finder: TypedFindView) {\n\n  lazy val rootView = finder.findView(TR.app_link_root)\n\n  lazy val loadingView = finder.findView(TR.app_link_loading_layout)\n\n  lazy val loadingText = finder.findView(TR.app_link_loading_text)\n\n  lazy val collectionView = finder.findView(TR.app_link_collection)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/applinks/AppLinksReceiverJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.applinks\n\nimport android.net.Uri\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.ui.collections.tasks.CollectionJobs\nimport cards.nine.app.ui.commons.action_filters.CollectionAddedActionFilter\nimport cards.nine.app.ui.commons.{BroadAction, ImplicitsUiExceptions, Jobs}\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.SharedCollection\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ActivityContextWrapper\n\nclass AppLinksReceiverJobs(actions: AppLinksReceiverUiActions)(\n    implicit contextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with Conversions\n    with CollectionJobs\n    with ImplicitsUiExceptions {\n\n  def uriReceived(uri: Uri): TaskService[Unit] = {\n\n    def safeExtractPath: Option[String] =\n      Option(uri) flatMap (u => Option(u.getPath))\n\n    val CollectionsPathRegex = \"\\\\/shared-collection\\\\/(.+)\".r\n\n    def openInBrowser(uri: Uri): TaskService[Unit] =\n      for {\n        _ <- di.trackEventProcess.appLinkReceived(false)\n        _ <- actions.showLinkNotSupportedMessage()\n        _ <- di.launcherExecutorProcess.launchUrl(uri.toString)\n        _ <- actions.exit()\n      } yield ()\n\n    (safeExtractPath, Option(uri)) match {\n      case (Some(CollectionsPathRegex(id)), _) =>\n        for {\n          _                <- di.trackEventProcess.appLinkReceived(true)\n          theme            <- getThemeTask\n          _                <- actions.initializeView(theme)\n          sharedCollection <- di.sharedCollectionsProcess.getSharedCollection(id)\n          _                <- actions.showCollection(this, sharedCollection, theme)\n        } yield ()\n      case (_, Some(link)) =>\n        openInBrowser(link)\n      case (_, None) => actions.exit()\n    }\n\n  }\n\n  def addCollection(sharedCollection: SharedCollection): TaskService[Unit] =\n    for {\n      col <- addSharedCollection(sharedCollection)\n      _ <- sendBroadCastTask(\n        BroadAction(CollectionAddedActionFilter.action, Some(col.id.toString)))\n        .resolveLeftTo((): Unit)\n      _ <- actions.exit()\n    } yield ()\n\n  def showError(): TaskService[Unit] =\n    for {\n      _ <- actions.showUnexpectedErrorMessage()\n      _ <- actions.exit()\n    } yield ()\n\n  def shareCollection(sharedCollection: SharedCollection): TaskService[Unit] =\n    for {\n      _ <- di.launcherExecutorProcess.launchShare(\n        getString(R.string.shared_collection_url, sharedCollection.id))\n      _ <- actions.exit()\n    } yield ()\n\n  protected def getString(res: Int, format: AnyRef*) =\n    resGetString(res, format)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/applinks/AppLinksReceiverUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.applinks\n\nimport android.view.ViewGroup\nimport macroid.extras.UIActionsExtras._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.adapters.sharedcollections.SharedCollectionItem\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.theme.{CardLayoutBackgroundColor, CardTextColor}\nimport cards.nine.models.{NineCardsTheme, SharedCollection}\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\n\nclass AppLinksReceiverUiActions(dom: AppLinksReceiverDOM)(\n    implicit val context: ActivityContextWrapper,\n    val uiContext: UiContext[_])\n    extends SharedCollectionItem {\n\n  override def content: ViewGroup = dom.collectionView\n\n  def initializeView(theme: NineCardsTheme): TaskService[Unit] =\n    ((dom.rootView <~ vBackgroundColor(theme.get(CardLayoutBackgroundColor))) ~\n      (dom.loadingText <~ tvColor(theme.get(CardTextColor))) ~\n      initialize()(theme) ~\n      (dom.loadingView <~ vVisible) ~\n      (dom.collectionView <~ vGone)).toService()\n\n  def showCollection(\n      jobs: AppLinksReceiverJobs,\n      collection: SharedCollection,\n      theme: NineCardsTheme): TaskService[Unit] = {\n\n    def onAddCollection(): Unit =\n      jobs.addCollection(collection).resolveAsyncServiceOr(_ => jobs.showError())\n\n    def onShareCollection(): Unit =\n      jobs.shareCollection(collection).resolveAsyncServiceOr(_ => jobs.showError())\n\n    ((dom.loadingView <~ vGone) ~\n      (dom.collectionView <~ vVisible) ~\n      bind(collection, onAddCollection(), onShareCollection())(theme)).toService()\n  }\n\n  def showLinkNotSupportedMessage(): TaskService[Unit] =\n    uiShortToast(R.string.linkNotSupportedError).toService()\n\n  def showUnexpectedErrorMessage(): TaskService[Unit] =\n    uiShortToast(R.string.contactUsError).toService()\n\n  def exit(): TaskService[Unit] =\n    Ui(context.original.get foreach (_.finish())).toService()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/CollectionAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections\n\nimport android.graphics.drawable._\nimport android.graphics.drawable.shapes.OvalShape\nimport android.support.v7.widget.RecyclerView.ViewHolder\nimport android.support.v7.widget.{CardView, RecyclerView}\nimport android.view.ViewGroup.LayoutParams._\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport android.widget.ImageView.ScaleType\nimport android.widget.{FrameLayout, TextView}\nimport cards.nine.app.ui.collections.CollectionsDetailsActivity._\nimport cards.nine.app.ui.collections.jobs.EditingCollectionMode\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport macroid.extras.UIActionsExtras._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.styles.CommonStyles\nimport cards.nine.app.ui.components.commons.ReorderItemTouchListener\nimport cards.nine.app.ui.components.drawables.{\n  BackgroundSelectedDrawable,\n  IconTypes,\n  PathMorphDrawable\n}\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.app.ui.preferences.commons.{FontSize, IconsSize, ShowPositionInCards}\nimport cards.nine.commons._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.commons.ops.SeqOps._\nimport cards.nine.models.types._\nimport cards.nine.models.types.theme.{CardBackgroundColor, CardTextColor}\nimport cards.nine.models.{Card, Collection, NineCardsTheme}\nimport macroid.extras.CardViewTweaks._\nimport macroid.extras.FrameLayoutTweaks._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid.{ActivityContextWrapper, Ui, _}\n\ncase class CollectionAdapter(\n    var collection: Collection,\n    heightCard: Int,\n    onClick: (Card, Int) => Unit,\n    onLongClick: (ViewHolder) => Unit)(\n    implicit activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[ViewHolderCollectionAdapter]\n    with ReorderItemTouchListener { self =>\n\n  val showPositions = ShowPositionInCards.readValue\n\n  override def onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderCollectionAdapter = {\n    val view = LayoutInflater.from(parent.getContext).inflate(TR.layout.card_item, parent, false)\n    ViewHolderCollectionAdapter(\n      content = view,\n      heightCard = heightCard,\n      showPositions = showPositions,\n      onLongClick = onLongClick)\n  }\n\n  override def getItemCount: Int = collection.cards.size\n\n  override def onBindViewHolder(viewHolder: ViewHolderCollectionAdapter, position: Int): Unit =\n    viewHolder.bind(collection.cards(position), onClick).run\n\n  def addCards(cards: Seq[Card]): Unit = {\n    collection = collection.copy(cards = collection.cards ++ cards)\n    val count = cards.length\n    notifyItemRangeInserted(collection.cards.length - count, count)\n  }\n\n  def removeCards(cards: Seq[Card]): Unit = {\n    val cardIds = cards map (_.id)\n    collection = collection.copy(cards = collection.cards.filterNot(c => cardIds.contains(c.id)))\n    notifyDataSetChanged()\n  }\n\n  def updateCard(card: Card): Unit = {\n    val position = card.position\n    collection = collection.copy(cards = collection.cards.updated(position, card))\n    notifyItemChanged(position, card)\n  }\n\n  def updateCards(cards: Seq[Card]): Unit = {\n    collection = collection.copy(cards = cards)\n    notifyItemRangeChanged(0, cards.length)\n  }\n\n  override def onItemMove(from: Int, to: Int): Unit = {\n    collection = collection.copy(cards = collection.cards.reorder(from, to))\n    notifyItemMoved(from, to)\n  }\n}\n\ncase class ViewHolderCollectionAdapter(\n    content: CardView,\n    heightCard: Int,\n    showPositions: Boolean,\n    onLongClick: (ViewHolder) => Unit)(\n    implicit context: ActivityContextWrapper,\n    theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with CollectionAdapterStyles\n    with TypedFindView {\n\n  lazy val iconContent = findView(TR.card_icon_content)\n\n  lazy val icon = findView(TR.card_icon)\n\n  lazy val name = findView(TR.card_text)\n\n  lazy val badge = findView(TR.card_badge)\n\n  lazy val selectedIcon = findView(TR.card_selected)\n\n  val iconSelectedDrawable = PathMorphDrawable(\n    defaultIcon = IconTypes.CHECK,\n    defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_thin),\n    padding = resGetDimensionPixelSize(R.dimen.padding_small))\n\n  val selectedBackground = new BackgroundSelectedDrawable\n\n  ((content <~\n    rootStyle(heightCard) <~\n    On.longClick {\n      Ui {\n        onLongClick(this)\n        true\n      }\n    }) ~\n    (selectedIcon <~ vBackground(selectedBackground)) ~\n    (iconContent <~ iconContentStyle(heightCard))).run\n\n  def bind(card: Card, onClick: (Card, Int) => Unit)(implicit uiContext: UiContext[_]): Ui[_] = {\n    val selectedViewUi = statuses.collectionMode match {\n      case EditingCollectionMode =>\n        selectCard(statuses.positionsEditing.contains(getAdapterPosition))\n      case _ =>\n        if (selectedIcon.getVisibility == View.VISIBLE) clearSelectedCard()\n        else Ui.nop\n    }\n    val text =\n      if (showPositions) s\"${card.position} - ${card.term}\" else card.term\n    (content <~ On.click {\n      Ui(onClick(card, getAdapterPosition))\n    }) ~\n      (icon <~ vResize(IconsSize.getIconApp) <~ iconCardTransform(card)) ~\n      (name <~ tvText(text) <~ tvSizeResource(FontSize.getSizeResource) <~ nameStyle(\n        card.cardType)) ~\n      (badge <~ (getBadge(card.cardType) map {\n        ivSrc(_) + vVisible\n      } getOrElse vGone)) ~\n      selectedViewUi\n  }\n\n  def selectCard(select: Boolean) = {\n    selectedBackground.selected(select)\n    selectedIcon <~ vVisible <~ (if (select) ivSrc(iconSelectedDrawable)\n                                 else ivBlank)\n  }\n\n  def clearSelectedCard() = selectedIcon <~ vGone\n\n  private[this] def getBadge(cardType: CardType): Option[Int] =\n    cardType match {\n      case PhoneCardType => Option(R.drawable.badge_phone)\n      case SmsCardType   => Option(R.drawable.badge_sms)\n      case EmailCardType => Option(R.drawable.badge_email)\n      case _             => None\n    }\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n\n}\n\ntrait CollectionAdapterStyles extends CommonStyles {\n\n  val iconContentHeightRatio = .6f\n\n  def rootStyle(\n      heightCard: Int)(implicit context: ContextWrapper, theme: NineCardsTheme): Tweak[CardView] =\n    Tweak[CardView] { view =>\n      view.getLayoutParams.height = heightCard\n    } +\n      cvCardBackgroundColor(theme.get(CardBackgroundColor)) +\n      flForeground(createBackground) +\n      vDisableHapticFeedback\n\n  def iconContentStyle(heightCard: Int)(implicit context: ContextWrapper): Tweak[FrameLayout] =\n    Tweak[FrameLayout] { view =>\n      view.getLayoutParams.height = (heightCard * iconContentHeightRatio).toInt\n    }\n\n  def nameStyle(cardType: CardType)(\n      implicit context: ContextWrapper,\n      theme: NineCardsTheme): Tweak[TextView] =\n    cardType match {\n      case NoInstalledAppCardType =>\n        tvColor(theme.get(CardTextColor).alpha(.4f))\n      case _ =>\n        tvColor(theme.get(CardTextColor))\n    }\n\n  def iconCardTransform(card: Card)(\n      implicit context: ActivityContextWrapper,\n      uiContext: UiContext[_],\n      theme: NineCardsTheme) =\n    card.cardType match {\n      case cardType if cardType.isContact =>\n        ivUriContactFromLookup(card.intent.extractLookup(), card.term) +\n          vBackground(javaNull) +\n          expandLayout +\n          ivScaleType(ScaleType.CENTER_CROP)\n      case AppCardType => ivSrcByPackageName(card.packageName, card.term)\n      case NoInstalledAppCardType =>\n        val shape = new ShapeDrawable(new OvalShape)\n        shape.getPaint.setColor(theme.get(CardTextColor).alpha(.4f))\n        val iconColor = theme.get(CardBackgroundColor)\n        ivSrc(R.drawable.icon_card_not_installed) +\n          tivDefaultColor(iconColor) +\n          tivPressedColor(iconColor) +\n          vBackground(shape) +\n          reduceLayout +\n          ivScaleType(ScaleType.CENTER_INSIDE)\n      case _ =>\n        ivCardUri(card.imagePath, card.term, circular = true) +\n          vBackground(javaNull) +\n          reduceLayout +\n          ivScaleType(ScaleType.FIT_CENTER)\n    }\n\n  private[this] def expandLayout(implicit context: ContextWrapper): Tweak[View] = Tweak[View] {\n    view =>\n      val params = view.getLayoutParams\n      params.height = MATCH_PARENT\n      params.width = MATCH_PARENT\n      view.requestLayout()\n  }\n\n  private[this] def reduceLayout(implicit context: ContextWrapper): Tweak[View] =\n    vResize(IconsSize.getIconApp)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/CollectionFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections\n\nimport android.os.Bundle\nimport android.support.v4.app.Fragment\nimport android.support.v7.widget.RecyclerView.ViewHolder\nimport android.view._\nimport cards.nine.app.commons.ContextSupportProvider\nimport cards.nine.app.ui.collections.CollectionFragment._\nimport cards.nine.app.ui.collections.CollectionsDetailsActivity._\nimport cards.nine.app.ui.collections.jobs._\nimport cards.nine.app.ui.collections.jobs.uiactions._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.{FragmentUiContext, UiContext, UiExtensions}\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.javaNull\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.{PhoneCardType, PublishedByMe}\nimport cards.nine.models.{Card, Collection}\nimport cards.nine.process.intents.LauncherExecutorProcessPermissionException\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{TR, _}\nimport macroid.Contexts\n\nimport scala.language.postfixOps\n\nclass CollectionFragment\n    extends Fragment\n    with Contexts[Fragment]\n    with ContextSupportProvider\n    with UiExtensions\n    with TypedFindView\n    with SingleCollectionDOM\n    with SingleCollectionUiListener { self =>\n\n  val badActivityMessage =\n    \"CollectionFragment only can be loaded in CollectionsDetailsActivity\"\n\n  implicit lazy val uiContext: UiContext[Fragment] = FragmentUiContext(self)\n\n  lazy val actions = new SingleCollectionUiActions(self, self)\n\n  lazy val singleCollectionJobs = new SingleCollectionJobs(\n    animateCards = getBoolean(Seq(getArguments), keyAnimateCards, default = false),\n    maybeCollection = Option(getSerialize[Collection](Seq(getArguments), keyCollection, javaNull)),\n    actions = actions)\n\n  lazy val groupCollectionsJobs: GroupCollectionsJobs = getActivity match {\n    case activity: CollectionsDetailsActivity => activity.groupCollectionsJobs\n    case _                                    => throw new IllegalArgumentException(badActivityMessage)\n  }\n\n  lazy val toolbarJobs: ToolbarJobs = getActivity match {\n    case activity: CollectionsDetailsActivity => activity.toolbarJobs\n    case _                                    => throw new IllegalArgumentException(badActivityMessage)\n  }\n\n  protected var rootView: Option[View] = None\n\n  def isActiveFragment: Boolean =\n    actions.singleCollectionStatuses.activeFragment\n\n  def setActiveFragment(activeFragment: Boolean) =\n    actions.singleCollectionStatuses =\n      actions.singleCollectionStatuses.copy(activeFragment = activeFragment)\n\n  override protected def findViewById(id: Int): View =\n    rootView map (_.findViewById(id)) orNull\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n    setHasOptionsMenu(true)\n  }\n\n  override def onDestroy(): Unit = {\n    super.onDestroy()\n    singleCollectionJobs.removeCollectionIdForShortcut().resolveAsync()\n  }\n\n  override def onCreateView(\n      inflater: LayoutInflater,\n      container: ViewGroup,\n      savedInstanceState: Bundle): View = {\n    val baseView = LayoutInflater\n      .from(getActivity)\n      .inflate(TR.layout.collection_detail_fragment, container, false)\n    rootView = Some(baseView)\n    baseView\n  }\n\n  override def onViewCreated(view: View, savedInstanceState: Bundle): Unit = {\n    (for {\n      _ <- singleCollectionJobs.initialize()\n      _ <- singleCollectionJobs.showData()\n    } yield ()).resolveAsync()\n    super.onViewCreated(view, savedInstanceState)\n  }\n\n  override def onCreateOptionsMenu(menu: Menu, inflater: MenuInflater): Unit = {\n    inflater.inflate(R.menu.collection_edit_menu, menu)\n    super.onCreateOptionsMenu(menu, inflater)\n  }\n\n  override def onPrepareOptionsMenu(menu: Menu): Unit = {\n    super.onPrepareOptionsMenu(menu)\n    (statuses.collectionMode, statuses.positionsEditing.toSeq.length) match {\n      case (NormalCollectionMode, _) =>\n        statuses.publishStatus match {\n          case PublishedByMe =>\n            menu\n              .findItem(R.id.action_make_public)\n              .setEnabled(false)\n              .setTitle(resGetString(R.string.alreadyPublishedCollection))\n            menu.findItem(R.id.action_share).setVisible(true)\n          case _ =>\n            menu\n              .findItem(R.id.action_make_public)\n              .setEnabled(true)\n              .setTitle(resGetString(R.string.make_public))\n            menu.findItem(R.id.action_share).setVisible(false)\n        }\n        menu.findItem(R.id.action_add_apps).setVisible(true)\n        menu.findItem(R.id.action_add_contact).setVisible(true)\n        menu.findItem(R.id.action_add_recommendation).setVisible(true)\n        menu.findItem(R.id.action_add_shortcut).setVisible(true)\n        menu.findItem(R.id.action_edit).setVisible(false)\n        menu.findItem(R.id.action_move_to_collection).setVisible(false)\n        menu.findItem(R.id.action_delete).setVisible(false)\n      case (EditingCollectionMode, 1) =>\n        menu.findItem(R.id.action_add_apps).setVisible(false)\n        menu.findItem(R.id.action_add_contact).setVisible(false)\n        menu.findItem(R.id.action_add_recommendation).setVisible(false)\n        menu.findItem(R.id.action_add_shortcut).setVisible(false)\n        menu.findItem(R.id.action_make_public).setVisible(false)\n        menu.findItem(R.id.action_share).setVisible(false)\n        menu.findItem(R.id.action_edit).setVisible(true)\n        menu.findItem(R.id.action_move_to_collection).setVisible(true)\n        menu.findItem(R.id.action_delete).setVisible(true)\n      case (EditingCollectionMode, _) =>\n        menu.findItem(R.id.action_add_apps).setVisible(false)\n        menu.findItem(R.id.action_add_contact).setVisible(false)\n        menu.findItem(R.id.action_add_recommendation).setVisible(false)\n        menu.findItem(R.id.action_add_shortcut).setVisible(false)\n        menu.findItem(R.id.action_make_public).setVisible(false)\n        menu.findItem(R.id.action_share).setVisible(false)\n        menu.findItem(R.id.action_edit).setVisible(false)\n        menu.findItem(R.id.action_move_to_collection).setVisible(true)\n        menu.findItem(R.id.action_delete).setVisible(true)\n    }\n  }\n\n  override def onOptionsItemSelected(item: MenuItem): Boolean =\n    item.getItemId match {\n      case R.id.action_edit =>\n        groupCollectionsJobs.editCard().resolveAsync()\n        true\n      case R.id.action_move_to_collection =>\n        singleCollectionJobs\n          .moveToCollection()\n          .resolveAsyncServiceOr(_ => singleCollectionJobs.showGenericError())\n        true\n      case R.id.action_delete =>\n        (for {\n          cards <- groupCollectionsJobs.removeCardsInEditMode()\n          _     <- singleCollectionJobs.removeCards(cards)\n        } yield ()).resolveAsync()\n        true\n      case _ => super.onOptionsItemSelected(item)\n    }\n\n  override def reorderCard(collectionId: Int, cardId: Int, position: Int): Unit =\n    singleCollectionJobs.reorderCard(collectionId, cardId, position).resolveAsync()\n\n  override def scrollStateChanged(idDragging: Boolean): Unit =\n    groupCollectionsJobs.showMenu().resolveIf(idDragging, ()).resolveAsync()\n\n  override def close(): Unit = groupCollectionsJobs.close().resolveAsync()\n\n  override def pullToClose(scroll: Int, close: Boolean): Unit =\n    toolbarJobs.pullToClose(scroll, close).resolveAsync()\n\n  override def reloadCards(): Unit =\n    groupCollectionsJobs.reloadCards().resolveAsync()\n\n  override def moveToCollection(toCollectionId: Int, collectionPosition: Int): Unit =\n    (for {\n      cards <- groupCollectionsJobs.moveToCollection(toCollectionId, collectionPosition)\n      _     <- singleCollectionJobs.removeCards(cards)\n    } yield ()).resolveAsync()\n\n  override def firstItemInCollection(): Unit =\n    groupCollectionsJobs.firstItemInCollection().resolveAsync()\n\n  override def emptyCollection(): Unit =\n    groupCollectionsJobs.emptyCollection().resolveAsync()\n\n  def openReorderMode(): Unit =\n    groupCollectionsJobs.openReorderMode().resolveAsync()\n\n  def closeReorderMode(position: Int): Unit =\n    groupCollectionsJobs.closeReorderMode(position).resolveAsync()\n\n  def startReorderCards(holder: ViewHolder): Unit =\n    singleCollectionJobs.startReorderCards(holder).resolveAsync()\n\n  override def performCard(card: Card, position: Int): Unit =\n    groupCollectionsJobs.performCard(card, position).resolveAsyncServiceOr[Throwable] {\n      case _: LauncherExecutorProcessPermissionException if card.cardType == PhoneCardType =>\n        groupCollectionsJobs.requestCallPhonePermission(card.intent.extractPhone())\n      case _ =>\n        groupCollectionsJobs.groupCollectionsUiActions.showContactUsError()\n    }\n}\n\nobject CollectionFragment {\n  val keyPosition     = \"tab_position\"\n  val keyCollection   = \"collection\"\n  val keyCollectionId = \"collection_id\"\n  val keyAnimateCards = \"animate_cards\"\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/CollectionsDetailsActivity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.support.v7.app.AppCompatActivity\nimport android.view._\nimport cats.implicits._\nimport cards.nine.app.commons._\nimport cards.nine.app.ui.collections.CollectionsDetailsActivity._\nimport cards.nine.app.ui.collections.jobs._\nimport cards.nine.app.ui.collections.jobs.uiactions._\nimport cards.nine.app.ui.commons.RequestCodes._\nimport cards.nine.app.ui.commons.action_filters.{AppInstalledActionFilter, AppsActionFilter}\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.{ActivityUiContext, AppUtils, UiContext, UiExtensions}\nimport cards.nine.app.ui.components.drawables.PathMorphDrawable\nimport cards.nine.app.ui.preferences.commons.{\n  CircleOpeningCollectionAnimation,\n  CollectionOpeningAnimations\n}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.{NotPublished, PublicCollectionStatus}\nimport cards.nine.models.{Card, CardData, Collection, NineCardsTheme}\nimport com.fortysevendeg.ninecardslauncher.{R, TypedFindView}\nimport macroid._\n\nclass CollectionsDetailsActivity\n    extends AppCompatActivity\n    with Contexts[AppCompatActivity]\n    with ContextSupportProvider\n    with GroupCollectionsUiListener\n    with TypedFindView\n    with UiExtensions\n    with BroadcastDispatcher { self =>\n\n  val defaultPosition = 0\n\n  val defaultIndexColor = 0\n\n  val defaultIcon = \"\"\n\n  val defaultStateChanged = false\n\n  var firstTime = true\n\n  implicit lazy val uiContext: UiContext[Activity] = ActivityUiContext(this)\n\n  implicit lazy val groupCollectionsJobs = createGroupCollectionsJobs\n\n  implicit lazy val toolbarJobs = createToolbarJobs\n\n  implicit lazy val sharedCollectionJobs = createSharedCollectionJobs\n\n  lazy val navigationJobs = createNavigationJobs\n\n  implicit def getSingleCollectionJobs: Option[SingleCollectionJobs] =\n    createSingleCollectionJobs(groupCollectionsJobs.groupCollectionsUiActions.dom)\n\n  def getSingleCollectionJobsByPosition(position: Int): Option[SingleCollectionJobs] =\n    createSingleCollectionJobsByPosition(\n      groupCollectionsJobs.groupCollectionsUiActions.dom,\n      position)\n\n  override val actionsFilters: Seq[String] = AppsActionFilter.cases map (_.action)\n\n  override def manageCommand(action: String, data: Option[String]): Unit =\n    (AppsActionFilter(action), data) match {\n      case (Some(AppInstalledActionFilter), _) =>\n        (for {\n          cards <- groupCollectionsJobs.reloadCards()\n          _ <- getSingleCollectionJobs match {\n            case Some(singleCollectionJobs) =>\n              singleCollectionJobs.reloadCards(cards)\n            case _ => TaskService.empty\n          }\n        } yield ()).resolveAsync()\n      case _ =>\n    }\n\n  override def onCreate(bundle: Bundle) = {\n    super.onCreate(bundle)\n\n    statuses = statuses.reset()\n\n    val position = getInt(Seq(bundle, getIntent.getExtras), startPositionKey, defaultPosition)\n\n    val initialToolbarColor =\n      getInt(Seq(bundle, getIntent.getExtras), toolbarColorKey, defaultIndexColor)\n\n    val backgroundColor =\n      getInt(Seq(bundle, getIntent.getExtras), backgroundColorKey, defaultIndexColor)\n\n    val icon =\n      getString(Seq(bundle, getIntent.getExtras), toolbarIconKey, defaultIcon)\n\n    val isStateChanged =\n      getBoolean(Seq(bundle, getIntent.getExtras), stateChangedKey, defaultStateChanged)\n\n    setContentView(R.layout.collections_detail_activity)\n\n    groupCollectionsJobs\n      .initialize(backgroundColor, initialToolbarColor, icon, position, isStateChanged)\n      .resolveAsync(\n        onResult = (_) =>\n          groupCollectionsJobs.groupCollectionsUiActions\n            .openCollectionsWizardInline()\n            .resolveAsync(),\n        onException = (_) =>\n          groupCollectionsJobs.groupCollectionsUiActions.showContactUsError().resolveAsync())\n\n    registerDispatchers()\n\n  }\n\n  override def onResume(): Unit = {\n    val anim = CollectionOpeningAnimations.readValue\n    if (firstTime && anim == CircleOpeningCollectionAnimation && anim.isSupported) {\n      overridePendingTransition(0, 0)\n      firstTime = false\n    } else {\n      overridePendingTransition(\n        R.anim.abc_grow_fade_in_from_bottom,\n        R.anim.abc_shrink_fade_out_from_bottom)\n    }\n    super.onResume()\n    groupCollectionsJobs.resume().resolveAsync()\n  }\n\n  override def onPause(): Unit = {\n    overridePendingTransition(\n      R.anim.abc_grow_fade_in_from_bottom,\n      R.anim.abc_shrink_fade_out_from_bottom)\n    super.onPause()\n    groupCollectionsJobs.pause().resolveAsync()\n  }\n\n  override def onDestroy(): Unit = {\n    super.onDestroy()\n    unregisterDispatcher()\n  }\n\n  override def onSaveInstanceState(outState: Bundle): Unit = {\n    outState.putInt(\n      startPositionKey,\n      groupCollectionsJobs.groupCollectionsUiActions.dom.getCurrentPosition getOrElse defaultPosition)\n    outState.putBoolean(stateChangedKey, true)\n    groupCollectionsJobs.groupCollectionsUiActions.dom.getCurrentCollection foreach { collection =>\n      outState.putInt(toolbarColorKey, collection.themedColorIndex)\n      outState.putString(toolbarIconKey, collection.icon)\n    }\n    super.onSaveInstanceState(outState)\n  }\n\n  override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit = {\n    super.onActivityResult(requestCode, resultCode, data)\n    requestCode match {\n      case `shortcutAdded` =>\n        (for {\n          maybeCard <- groupCollectionsJobs.addShortcut(data)\n          _ <- (maybeCard, getSingleCollectionJobs) match {\n            case (Some(card), Some(singleCollectionJobs)) =>\n              singleCollectionJobs.addCards(Seq(card)) *> singleCollectionJobs\n                .removeCollectionIdForShortcut()\n            case _ => TaskService.empty\n          }\n        } yield ()).resolveAsyncServiceOr(_ =>\n          groupCollectionsJobs.groupCollectionsUiActions.showContactUsError())\n      case _ =>\n    }\n  }\n\n  override def onCreateOptionsMenu(menu: Menu): Boolean = {\n    getMenuInflater.inflate(R.menu.collection_detail_menu, menu)\n    super.onCreateOptionsMenu(menu)\n  }\n\n  override def onPrepareOptionsMenu(menu: Menu): Boolean = {\n    groupCollectionsJobs.savePublishStatus().resolveAsync()\n    super.onPrepareOptionsMenu(menu)\n  }\n\n  override def onOptionsItemSelected(item: MenuItem): Boolean =\n    item.getItemId match {\n      case android.R.id.home =>\n        groupCollectionsJobs.close().resolveAsync()\n        false\n      case R.id.action_add_apps =>\n        navigationJobs.showAppDialog().resolveAsync()\n        true\n      case R.id.action_add_contact =>\n        navigationJobs.showContactsDialog().resolveAsync()\n        true\n      case R.id.action_add_recommendation =>\n        navigationJobs.showRecommendationDialog().resolveAsync()\n        true\n      case R.id.action_add_shortcut =>\n        navigationJobs.showShortcutDialog().resolveAsync()\n        true\n      case R.id.action_make_public =>\n        sharedCollectionJobs\n          .showPublishCollectionWizard()\n          .resolveAsyncServiceOr(_ =>\n            groupCollectionsJobs.groupCollectionsUiActions.showContactUsError())\n        true\n      case R.id.action_share =>\n        sharedCollectionJobs\n          .shareCollection()\n          .resolveAsyncServiceOr(_ =>\n            groupCollectionsJobs.groupCollectionsUiActions.showContactUsError())\n        true\n      case _ => super.onOptionsItemSelected(item)\n    }\n\n  override def onRequestPermissionsResult(\n      requestCode: Int,\n      permissions: Array[String],\n      grantResults: Array[Int]): Unit = {\n    super.onRequestPermissionsResult(requestCode, permissions, grantResults)\n    groupCollectionsJobs\n      .requestPermissionsResult(requestCode, permissions, grantResults)\n      .resolveAsyncServiceOr(_ =>\n        groupCollectionsJobs.groupCollectionsUiActions.showContactUsError())\n  }\n\n  override def onBackPressed(): Unit =\n    groupCollectionsJobs.back().resolveAsync()\n\n  override def closeEditingMode(): Unit =\n    statuses.collectionMode match {\n      case EditingCollectionMode =>\n        groupCollectionsJobs.closeEditingMode().resolveAsync()\n      case _ =>\n    }\n\n  override def isNormalMode: Boolean =\n    statuses.collectionMode == NormalCollectionMode\n\n  override def isEditingMode: Boolean =\n    statuses.collectionMode == EditingCollectionMode\n\n  override def showPublicCollectionDialog(collection: Collection): Unit =\n    groupCollectionsJobs.navigationUiActions.openPublishCollection(collection).resolveAsync()\n\n  def showEditCollectionDialog(cardName: String, onChangeName: (Option[String]) => Unit): Unit =\n    groupCollectionsJobs.navigationUiActions.openEditCard(cardName, onChangeName).resolveAsync()\n\n  override def addCards(cardsRequest: Seq[CardData]): Unit =\n    (for {\n      cards <- groupCollectionsJobs.addCards(cardsRequest)\n      _ <- getSingleCollectionJobs match {\n        case Some(singleCollectionJobs) => singleCollectionJobs.addCards(cards)\n        case _                          => TaskService.empty\n      }\n    } yield ()).resolveAsync()\n\n  override def bindAnimatedAdapter(): Unit =\n    getSingleCollectionJobs foreach (_.bindAnimatedAdapter().resolveAsync())\n\n  override def reloadCards(cards: Seq[Card]): Unit =\n    getSingleCollectionJobs foreach (_.reloadCards(cards).resolveAsync())\n\n  override def saveEditedCard(collectionId: Int, cardId: Int, cardName: Option[String]): Unit =\n    getSingleCollectionJobs foreach { job =>\n      job\n        .saveEditedCard(collectionId, cardId, cardName)\n        .resolveAsyncServiceOr(_ => job.showGenericError())\n    }\n\n  override def showDataInPosition(position: Int): Unit =\n    getSingleCollectionJobsByPosition(position) foreach (_.showData().resolveAsync())\n\n  override def showAppsDialog(): Unit =\n    (for {\n      _ <- groupCollectionsJobs.groupCollectionsUiActions.hideMenu()\n      _ <- navigationJobs.showAppDialog()\n    } yield ()).resolveAsync()\n\n  override def showContactsDialog(): Unit =\n    (for {\n      _ <- groupCollectionsJobs.groupCollectionsUiActions.hideMenu()\n      _ <- navigationJobs.showContactsDialog()\n    } yield ()).resolveAsync()\n\n  override def showShortcutsDialog(): Unit =\n    (for {\n      _ <- groupCollectionsJobs.groupCollectionsUiActions.hideMenu()\n      _ <- navigationJobs.showShortcutDialog()\n    } yield ()).resolveAsync()\n\n  override def showRecommendationsDialog(): Unit =\n    (for {\n      _ <- groupCollectionsJobs.groupCollectionsUiActions.hideMenu()\n      _ <- navigationJobs.showRecommendationDialog()\n    } yield ()).resolveAsync()\n\n}\n\ntrait ActionsScreenListener {\n  def onStartFinishAction()\n\n  def onEndFinishAction()\n}\n\nobject CollectionsDetailsActivity {\n\n  var statuses = CollectionsDetailsStatuses()\n\n  def createGroupCollectionsJobs(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]) = {\n    val dom      = new GroupCollectionsDOM(activityContextWrapper.getOriginal)\n    val listener = activityContextWrapper.getOriginal.asInstanceOf[GroupCollectionsUiListener]\n    new GroupCollectionsJobs(\n      groupCollectionsUiActions = new GroupCollectionsUiActions(dom, listener),\n      toolbarUiActions = new ToolbarUiActions(dom, listener),\n      navigationUiActions = new NavigationUiActions(dom))\n  }\n\n  def createToolbarJobs(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]) = {\n    val dom      = new GroupCollectionsDOM(activityContextWrapper.getOriginal)\n    val listener = activityContextWrapper.getOriginal.asInstanceOf[GroupCollectionsUiListener]\n    new ToolbarJobs(new ToolbarUiActions(dom, listener))\n  }\n\n  def createNavigationJobs(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]) = {\n    val dom      = new GroupCollectionsDOM(activityContextWrapper.getOriginal)\n    val listener = activityContextWrapper.getOriginal.asInstanceOf[GroupCollectionsUiListener]\n    new NavigationJobs(\n      groupCollectionsUiActions = new GroupCollectionsUiActions(dom, listener),\n      navigationUiActions = new NavigationUiActions(dom))\n  }\n\n  def createSharedCollectionJobs(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]) = {\n    val dom      = new GroupCollectionsDOM(activityContextWrapper.getOriginal)\n    val listener = activityContextWrapper.getOriginal.asInstanceOf[GroupCollectionsUiListener]\n    new SharedCollectionJobs(new SharedCollectionUiActions(dom, listener))\n  }\n\n  def createSingleCollectionJobs(dom: GroupCollectionsDOM): Option[SingleCollectionJobs] =\n    dom.getAdapter flatMap (_.getActiveFragment) map (_.singleCollectionJobs)\n\n  def createSingleCollectionJobsByPosition(\n      dom: GroupCollectionsDOM,\n      position: Int): Option[SingleCollectionJobs] =\n    dom.getAdapter flatMap (_.getFragmentByPosition(position)) map (_.singleCollectionJobs)\n\n  val startPositionKey   = \"start_position\"\n  val backgroundColorKey = \"color_background\"\n  val toolbarColorKey    = \"color_toolbar\"\n  val toolbarIconKey     = \"icon_toolbar\"\n  val stateChangedKey    = \"state_changed\"\n\n  val cardAdded = \"cardAdded\"\n\n  def getContentTransitionName(position: Int) = s\"icon_$position\"\n}\n\ncase class CollectionsDetailsStatuses(\n    theme: NineCardsTheme = AppUtils.getDefaultTheme,\n    iconHome: Option[PathMorphDrawable] = None,\n    collectionMode: CollectionMode = NormalCollectionMode,\n    positionsEditing: Set[Int] = Set.empty,\n    lastPhone: Option[String] = None,\n    publishStatus: PublicCollectionStatus = NotPublished) {\n\n  def getPositionsSelected: Int = positionsEditing.toSeq.length\n\n  def reset(): CollectionsDetailsStatuses =\n    copy(\n      collectionMode = NormalCollectionMode,\n      positionsEditing = Set.empty,\n      lastPhone = None,\n      publishStatus = NotPublished)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/CollectionsPagerAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections\n\nimport android.os.Bundle\nimport android.support.v4.app.{Fragment, FragmentManager, FragmentStatePagerAdapter}\nimport android.view.ViewGroup\nimport cards.nine.models.{Card, Collection, NineCardsTheme}\nimport macroid.{ContextWrapper, Ui}\n\nimport scala.collection.mutable\n\ncase class CollectionsPagerAdapter(\n    fragmentManager: FragmentManager,\n    var collections: Seq[Collection],\n    startPosition: Int)(implicit context: ContextWrapper, theme: NineCardsTheme)\n    extends FragmentStatePagerAdapter(fragmentManager) {\n\n  val fragments: mutable.WeakHashMap[Int, CollectionFragment] =\n    mutable.WeakHashMap.empty\n\n  var statuses = CollectionsPagerAdapterStatuses()\n\n  private[this] def firstTimeInStartPosition(position: Int) =\n    (statuses.firstTime, position == startPosition) match {\n      case (false, true) =>\n        statuses = statuses.copy(firstTime = true)\n        true\n      case _ => false\n    }\n\n  override def getItem(position: Int): Fragment = {\n    val fragment = new CollectionFragment\n    val bundle   = new Bundle()\n    bundle.putInt(CollectionFragment.keyPosition, position)\n    bundle.putBoolean(CollectionFragment.keyAnimateCards, firstTimeInStartPosition(position))\n    bundle.putSerializable(CollectionFragment.keyCollection, collections(position))\n    bundle.putInt(CollectionFragment.keyCollectionId, collections(position).id)\n    fragment.setArguments(bundle)\n    fragment\n  }\n\n  override def getCount: Int = collections.length\n\n  override def getPageTitle(position: Int): CharSequence =\n    collections(position).name\n\n  override def instantiateItem(container: ViewGroup, position: Int): AnyRef = {\n    val fragment = super.instantiateItem(container, position)\n    fragments.put(position, fragment.asInstanceOf[CollectionFragment])\n    fragment\n  }\n\n  override def destroyItem(container: ViewGroup, position: Int, `object`: scala.Any): Unit = {\n    fragments.remove(position)\n    super.destroyItem(container, position, `object`)\n  }\n\n  def addCardsToCollection(positionCollection: Int, cards: Seq[Card]): Unit = {\n    val currentCollection = collections(positionCollection)\n    val newCollection =\n      currentCollection.copy(cards = currentCollection.cards ++ cards)\n    collections = collections.patch(positionCollection, Seq(newCollection), 1)\n  }\n\n  def removeCardFromCollection(positionCollection: Int, cards: Seq[Card]): Unit = {\n    val currentCollection = collections(positionCollection)\n    val newCollection =\n      currentCollection.copy(cards = currentCollection.cards.filterNot(c => cards.contains(c)))\n    collections = collections.patch(positionCollection, Seq(newCollection), 1)\n  }\n\n  def updateCardFromCollection(positionCollection: Int, cards: Seq[Card]): Unit = {\n    val currentCollection = collections(positionCollection)\n    val newCollection     = currentCollection.copy(cards = cards)\n    collections = collections.patch(positionCollection, Seq(newCollection), 1)\n  }\n\n  def updateShareCollectionIdFromCollection(\n      positionCollection: Int,\n      sharedCollectionId: Option[String]): Unit = {\n    val currentCollection = collections(positionCollection)\n    val newCollection =\n      currentCollection.copy(sharedCollectionId = sharedCollectionId)\n    collections = collections.patch(positionCollection, Seq(newCollection), 1)\n  }\n\n  def getCurrentFragmentPosition: Option[Int] = fragments collectFirst {\n    case (id, fragment) if fragment.isActiveFragment => id\n  }\n\n  def getActiveFragment: Option[CollectionFragment] = fragments collectFirst {\n    case (_, fragment) if fragment.isActiveFragment => fragment\n  }\n\n  def getFragmentByPosition(position: Int): Option[CollectionFragment] =\n    fragments.find(_._1 == position).map(_._2)\n\n  def activateFragment(pos: Int): Unit = fragments foreach {\n    case (id, fragment) if id == pos => fragment.setActiveFragment(true)\n    case (_, fragment)               => fragment.setActiveFragment(false)\n  }\n\n  def notifyChanged(currentPosition: Int): Ui[_] = {\n    val uis = fragments map {\n      case (id, fragment) =>\n        id match {\n          case `currentPosition` =>\n            Ui(fragment.setActiveFragment(activeFragment = true))\n          case _ =>\n            Ui(fragment.setActiveFragment(activeFragment = false))\n        }\n    }\n    Ui.sequence(uis.toSeq: _*)\n  }\n\n  def clear(): Unit = fragments.clear()\n}\n\ncase class CollectionsPagerAdapterStatuses(firstTime: Boolean = false)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/dialog/EditCardDialogFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.dialog\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.content.DialogInterface.{OnClickListener, OnShowListener}\nimport android.os.Bundle\nimport android.support.v4.app.DialogFragment\nimport android.support.v7.app.AlertDialog\nimport android.view.LayoutInflater\nimport android.widget.LinearLayout\nimport macroid.extras.TextViewTweaks._\nimport cards.nine.app.ui.commons.CommonsExcerpt._\nimport macroid.extras.EditTextTweaks._\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid._\n\nclass EditCardDialogFragment(cardName: String, onChangeName: (Option[String]) => Unit)(\n    implicit contextWrapper: ContextWrapper)\n    extends DialogFragment {\n\n  override def onCreateDialog(savedInstanceState: Bundle): Dialog = {\n\n    val dialogView = new DialogView\n\n    val dialog = new AlertDialog.Builder(getActivity)\n      .setTitle(R.string.editCardDialogTitle)\n      .setView(dialogView)\n      .setPositiveButton(android.R.string.ok, new OnClickListener {\n        override def onClick(dialog: DialogInterface, which: Int): Unit =\n          onChangeName(dialogView.readText)\n      })\n      .setNegativeButton(android.R.string.cancel, javaNull)\n      .create()\n\n    dialog.setOnShowListener(new OnShowListener {\n      override def onShow(dialog: DialogInterface): Unit =\n        dialogView.showKeyboard.run\n    })\n\n    dialogView.setCardName(cardName).run\n    dialog\n  }\n\n  class DialogView extends LinearLayout(contextWrapper.bestAvailable) with TypedFindView {\n\n    LayoutInflater.from(getActivity).inflate(R.layout.dialog_edit_card, this)\n\n    private[this] lazy val editCardName = findView(TR.dialog_edit_card_name)\n\n    def setCardName(cardName: String): Ui[Any] =\n      editCardName <~ tvText(cardName)\n\n    def readText: Option[String] = (editCardName ~> text).get\n\n    def showKeyboard: Ui[Any] = editCardName <~ etShowKeyboard\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/dialog/publishcollection/PublishCollectionActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.dialog.publishcollection\n\nimport android.view.View\nimport android.widget.TextView\nimport cards.nine.app.ui.collections.CollectionsDetailsActivity._\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport macroid.extras.UIActionsExtras._\nimport macroid.extras.EditTextTweaks._\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons.dialogs.Styles\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.widgets.TintableImageView\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.types.NineCardsCategory\nimport cards.nine.models.types.theme.{DrawerIconColor, PrimaryColor}\nimport cards.nine.models.{Collection, _}\nimport macroid.extras.ProgressBarTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\n\nclass PublishCollectionActions(dom: PublishCollectionDOM with PublishCollectionUiListener)(\n    implicit activityContextWrapper: ActivityContextWrapper)\n    extends Styles\n    with PublishCollectionStyles {\n\n  val steps = 3\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  lazy val (categoryNamesMenu, categories) = {\n    val categoriesSorted = NineCardsCategory.appsCategories map { category =>\n      (resGetString(category.getStringResource) getOrElse category.name, category)\n    } sortBy (_._1)\n    (categoriesSorted map (_._1), categoriesSorted map (_._2))\n  }\n\n  def initialize(): TaskService[Unit] = {\n    val drawerIconColor = statuses.theme.get(DrawerIconColor)\n    ((dom.rootLayout <~ dialogBackgroundStyle) ~\n      (dom.startLayout <~ vVisible) ~\n      (dom.informationLayout <~ vInvisible) ~\n      (dom.publishingLayout <~ vInvisible) ~\n      (dom.endLayout <~ vInvisible) ~\n      (dom.startHeader <~ titleTextStyle) ~\n      (dom.startMessage <~ subtitleTextStyle) ~\n      (dom.informationHeader <~ titleTextStyle) ~\n      (dom.informationMessage <~ subtitleTextStyle) ~\n      (dom.collectionTag <~ subtitleTextStyle) ~\n      (dom.collectionName <~\n        titleTextStyle <~\n        etHintColor(drawerIconColor.alpha(0.3f))) ~\n      (dom.categoryTag <~ subtitleTextStyle) ~\n      (dom.publishButton <~ subtitleTextStyle) ~\n      (dom.publishingHeader <~ titleTextStyle) ~\n      (dom.publishingMessage <~ subtitleTextStyle) ~\n      (dom.endHeader <~ titleTextStyle) ~\n      (dom.endMessage <~ subtitleTextStyle) ~\n      (dom.startArrow <~\n        tivColor(drawerIconColor) <~\n        On.click(Ui(dom.showCollectionInformation()))) ~\n      (dom.collectionNameLine <~ iconStyle(0.5f)) ~\n      (dom.categoryIndicator <~ tivColor(drawerIconColor)) ~\n      (dom.categoryLine <~ iconStyle(0.5f)) ~\n      (dom.loading <~ pbColor(statuses.theme.get(PrimaryColor))) ~\n      (dom.endLine <~ iconStyle()) ~\n      (dom.endButton <~ subtitleTextStyle) ~\n      createPagers() ~\n      (dom.paginationPanel <~ reloadPagers(currentPage = 0))).toService()\n  }\n\n  def goToPublishCollectionInformation(collection: Collection): TaskService[Unit] =\n    ((dom.startLayout <~ applyFadeOut()) ~\n      (dom.informationLayout <~ applyFadeIn()) ~\n      (dom.publishingLayout <~ vInvisible) ~\n      (dom.endLayout <~ vInvisible) ~\n      (dom.collectionName <~ tvText(collection.name)) ~\n      (dom.categorySelect <~ categoryOnClick) ~\n      (dom.categorySpinner <~ spinnerStyle) ~\n      Ui(setCategory(collection.appsCategory)) ~\n      (dom.publishButton <~ publishOnClick) ~\n      (dom.paginationPanel <~ reloadPagers(currentPage = 1))).toService()\n\n  def goBackToPublishCollectionInformation(\n      name: String,\n      category: NineCardsCategory): TaskService[Unit] =\n    ((dom.startLayout <~ vInvisible) ~\n      (dom.informationLayout <~ applyFadeIn()) ~\n      (dom.publishingLayout <~ applyFadeOut()) ~\n      (dom.endLayout <~ vInvisible) ~\n      (dom.collectionName <~ tvText(name)) ~\n      (dom.categorySelect <~ categoryOnClick) ~\n      (dom.categorySpinner <~ spinnerStyle) ~\n      Ui(setCategory(Some(category))) ~\n      (dom.publishButton <~ publishOnClick) ~\n      (dom.paginationPanel <~ reloadPagers(currentPage = 1))).toService()\n\n  def goToPublishCollectionPublishing(): TaskService[Unit] =\n    ((dom.startLayout <~ vInvisible) ~\n      (dom.informationLayout <~ applyFadeOut()) ~\n      (dom.publishingLayout <~ applyFadeIn()) ~\n      (dom.endLayout <~ vInvisible) ~\n      (dom.paginationPanel <~ reloadPagers(currentPage = 1))).toService()\n\n  def goToPublishCollectionEnd(sharedCollectionId: String): TaskService[Unit] =\n    ((dom.startLayout <~ vInvisible) ~\n      (dom.informationLayout <~ vInvisible) ~\n      (dom.publishingLayout <~ applyFadeOut()) ~\n      (dom.paginationPanel <~ vInvisible) ~\n      (dom.endLayout <~ applyFadeIn()) ~\n      Ui(dom.reloadSharedCollectionId()) ~\n      (dom.endButton <~ On.click(\n        Ui(dom.launchShareCollection(sharedCollectionId)) ~ Ui(dom.dismiss())))).toService()\n\n  def showMessageCollectionError: TaskService[Unit] =\n    showMessage(R.string.collectionError).toService()\n\n  def showMessageFormFieldError: TaskService[Unit] =\n    showMessage(R.string.formFieldError).toService()\n\n  def showMessagePublishingError: TaskService[Unit] =\n    showMessage(R.string.publishingError).toService()\n\n  def showContactUsError: TaskService[Unit] =\n    showMessage(R.string.contactUsError).toService()\n\n  private[this] def showMessage(message: Int): Ui[Any] = uiShortToast(message)\n\n  private[this] def createPagers() = {\n    val pagerViews = (0 until steps) map pagination\n    dom.paginationPanel <~ vgAddViews(pagerViews)\n  }\n\n  private[this] def reloadPagers(currentPage: Int) = Transformer {\n    case i: TintableImageView\n        if Option(i.getTag).isDefined && i.getTag.equals(currentPage.toString) =>\n      i <~ tivColor(statuses.theme.get(DrawerIconColor).alpha(0.5f))\n    case i: TintableImageView =>\n      i <~ tivColor(statuses.theme.get(DrawerIconColor).alpha(0.2f))\n  }\n\n  private[this] def pagination(position: Int) =\n    (w[TintableImageView] <~ paginationItemStyle <~ vTag(position.toString)).get\n\n  private[this] def setCategory(maybeCategory: Option[NineCardsCategory]): Unit = {\n    maybeCategory foreach { category =>\n      dom.categorySpinner.setTag(category)\n      dom.categorySpinner.setText(categoryNamesMenu(categories.indexOf(category)))\n      dom.categorySpinner.setTextColor(statuses.theme.get(DrawerIconColor))\n    }\n  }\n\n  private[this] def categoryOnClick: Tweak[View] =\n    On.click {\n      dom.categorySpinner <~ vListThemedPopupWindowShow(\n        values = categoryNamesMenu,\n        onItemClickListener = (position: Int) => setCategory(Some(categories(position))),\n        width = Some(resGetDimensionPixelSize(R.dimen.width_list_popup_menu)),\n        height = Some(resGetDimensionPixelSize(R.dimen.height_list_popup_menu)))\n    }\n\n  private[this] def publishOnClick: Tweak[TextView] =\n    On.click(Ui(dom.publishCollection(dom.getName, dom.getCategory)))\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/dialog/publishcollection/PublishCollectionDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.dialog.publishcollection\n\nimport cards.nine.models.types.NineCardsCategory\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\n\ntrait PublishCollectionDOM { self: TypedFindView =>\n\n  lazy val rootLayout = findView(TR.publish_collection_wizard_root)\n\n  lazy val startLayout = findView(TR.publish_collection_wizard_start)\n\n  lazy val informationLayout = findView(TR.publish_collection_wizard_information)\n\n  lazy val publishingLayout = findView(TR.publish_collection_wizard_publishing)\n\n  lazy val endLayout = findView(TR.publish_collection_wizard_end)\n\n  lazy val startHeader = findView(TR.publish_collection_wizard_start_header)\n\n  lazy val startMessage = findView(TR.publish_collection_wizard_start_message)\n\n  lazy val startArrow = findView(TR.publish_collection_wizard_arrow)\n\n  lazy val informationHeader = findView(TR.publish_collection_wizard_information_header)\n\n  lazy val informationMessage = findView(TR.publish_collection_information_message)\n\n  lazy val collectionTag = findView(TR.collection_name_tag)\n\n  lazy val collectionInput = findView(TR.collection_name)\n\n  lazy val collectionName = findView(TR.collection_name)\n\n  lazy val collectionNameLine = findView(TR.collection_name_line)\n\n  lazy val categoryTag = findView(TR.category_tag)\n\n  lazy val categorySelect = findView(TR.category_select)\n\n  lazy val categorySpinner = findView(TR.category)\n\n  lazy val categoryIndicator = findView(TR.category_indicator)\n\n  lazy val categoryLine = findView(TR.category_line)\n\n  lazy val publishButton = findView(TR.publish_collection_wizard_information_button)\n\n  lazy val publishingHeader = findView(TR.publish_collection_wizard_publishing_header)\n\n  lazy val publishingMessage = findView(TR.publish_collection_wizard_publishing_message)\n\n  lazy val loading = findView(TR.publish_collection_loading)\n\n  lazy val endHeader = findView(TR.publish_collection_wizard_end_header)\n\n  lazy val endMessage = findView(TR.publish_collection_wizard_end_message)\n\n  lazy val endLine = findView(TR.end_line)\n\n  lazy val endButton = findView(TR.publish_collection_wizard_end_button)\n\n  lazy val paginationPanel = findView(TR.publish_collection_wizard_steps_pagination_panel)\n\n  def getName: Option[String] =\n    Option(collectionName.getText) flatMap {\n      case text if text.toString.nonEmpty => Option(text.toString)\n      case _                              => None\n    }\n\n  def getCategory: Option[NineCardsCategory] =\n    categorySpinner.getTag match {\n      case category: NineCardsCategory => Option(category)\n      case _                           => None\n    }\n\n}\n\ntrait PublishCollectionUiListener {\n\n  def showCollectionInformation(): Unit\n\n  def launchShareCollection(sharedCollectionId: String): Unit\n\n  def reloadSharedCollectionId(): Unit\n\n  def publishCollection(name: Option[String], category: Option[NineCardsCategory]): Unit\n\n  def dismiss(): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/dialog/publishcollection/PublishCollectionFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.dialog.publishcollection\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport android.support.v4.app.{DialogFragment, Fragment}\nimport android.support.v7.app.AlertDialog\nimport android.view.{LayoutInflater, View}\nimport android.widget.LinearLayout\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.collections.jobs.SharedCollectionJobs\nimport cards.nine.app.ui.commons.AppLog\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.models.Collection\nimport cards.nine.models.types.NineCardsCategory\nimport cards.nine.process.sharedcollections.SharedCollectionsConfigurationException\nimport com.fortysevendeg.ninecardslauncher.{R, TypedFindView}\nimport macroid._\n\nimport scala.language.postfixOps\n\ncase class PublishCollectionFragment(collection: Collection)(\n    implicit val sharedCollectionJobs: SharedCollectionJobs)\n    extends DialogFragment\n    with PublishCollectionDOM\n    with PublishCollectionUiListener\n    with TypedFindView\n    with Contexts[Fragment]\n    with AppNineCardsIntentConversions { self =>\n\n  lazy val actions = new PublishCollectionActions(self)\n\n  lazy val publishCollectionJobs = new PublishCollectionJobs(actions)\n\n  protected var rootView: Option[PublishCollectionWizardStartView] = None\n\n  override def onCreateDialog(savedInstanceState: Bundle): Dialog = {\n\n    val view = new PublishCollectionWizardStartView\n\n    rootView = Some(view)\n\n    publishCollectionJobs.initialize(collection).resolveAsync()\n\n    new AlertDialog.Builder(getActivity).setView(view).create()\n  }\n\n  class PublishCollectionWizardStartView\n      extends LinearLayout(fragmentContextWrapper.bestAvailable) {\n\n    LayoutInflater.from(getActivity).inflate(R.layout.publish_collection_wizard, this)\n\n  }\n\n  override protected def findViewById(id: Int): View =\n    rootView map (_.findViewById(id)) orNull\n\n  override def showCollectionInformation(): Unit =\n    publishCollectionJobs\n      .showCollectionInformation()\n      .resolveAsyncServiceOr(_ => publishCollectionJobs.showCollectionError())\n\n  override def launchShareCollection(sharedCollectionId: String): Unit =\n    publishCollectionJobs\n      .launchShareCollection(sharedCollectionId)\n      .resolveAsyncServiceOr(_ => publishCollectionJobs.showGenericError())\n\n  override def reloadSharedCollectionId(): Unit =\n    sharedCollectionJobs.reloadSharedCollectionId().resolveAsync()\n\n  override def publishCollection(name: Option[String], category: Option[NineCardsCategory]): Unit =\n    publishCollectionJobs.publishCollection(name, category).resolveAsyncServiceOr[Throwable] {\n      case e: SharedCollectionsConfigurationException =>\n        AppLog.invalidConfigurationV2\n        publishCollectionJobs.showPublishingError(name, category)\n      case _ => publishCollectionJobs.showPublishingError(name, category)\n    }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/dialog/publishcollection/PublishCollectionJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.dialog.publishcollection\n\nimport cats.implicits._\nimport cards.nine.app.ui.commons.{JobException, Jobs}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.Collection\nimport cards.nine.models.types.NineCardsCategory\nimport cards.nine.process.sharedcollections.SharedCollectionsException\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ActivityContextWrapper\n\nclass PublishCollectionJobs(actions: PublishCollectionActions)(\n    implicit val contextWrapper: ActivityContextWrapper)\n    extends Jobs {\n\n  var statuses = PublishCollectionStatuses()\n\n  def initialize(collection: Collection): TaskService[Unit] = {\n    statuses = statuses.copy(collection = Some(collection))\n    for {\n      _     <- di.trackEventProcess.publishCollectionByMenu(collection.name)\n      theme <- getThemeTask\n      _     <- actions.initialize()\n    } yield ()\n  }\n\n  def showCollectionInformation(): TaskService[Unit] = {\n    statuses.collection match {\n      case Some(collection) =>\n        actions.goToPublishCollectionInformation(collection)\n      case None => TaskService.left(JobException(\"Collection not found\"))\n    }\n  }\n\n  def publishCollection(\n      maybeName: Option[String],\n      maybeCategory: Option[NineCardsCategory]): TaskService[Unit] = {\n\n    def getCollection: TaskService[Collection] =\n      statuses.collection map TaskService.right getOrElse TaskService.left(\n        SharedCollectionsException(\"\", None))\n\n    def createPublishedCollection(name: String, category: NineCardsCategory): TaskService[String] =\n      for {\n        user       <- di.userProcess.getUser\n        collection <- getCollection\n        sharedCollectionId <- di.sharedCollectionsProcess.createSharedCollection(\n          author = user.userProfile.name getOrElse (user.email getOrElse resGetString(\n              R.string.defaultUser)),\n          name = name,\n          packages = collection.cards flatMap (_.packageName),\n          category = category,\n          icon = collection.icon,\n          community = false)\n        _ <- di.collectionProcess.updateSharedCollection(collection.id, sharedCollectionId)\n      } yield sharedCollectionId\n\n    (for {\n      name     <- maybeName\n      category <- maybeCategory\n    } yield {\n      for {\n        _                  <- actions.goToPublishCollectionPublishing()\n        sharedCollectionId <- createPublishedCollection(name, category)\n        _                  <- actions.goToPublishCollectionEnd(sharedCollectionId)\n      } yield ()\n    }) getOrElse actions.showMessageFormFieldError\n  }\n\n  def launchShareCollection(sharedCollectionId: String): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.shareCollectionAfterPublishing(sharedCollectionId)\n      _ <- di.launcherExecutorProcess.launchShare(\n        resGetString(R.string.shared_collection_url, sharedCollectionId))\n    } yield ()\n\n  def showPublishingError(\n      maybeName: Option[String],\n      maybeCategory: Option[NineCardsCategory]): TaskService[Unit] =\n    (for {\n      name     <- maybeName\n      category <- maybeCategory\n    } yield {\n      actions.showMessagePublishingError *> actions\n        .goBackToPublishCollectionInformation(name, category)\n    }) getOrElse actions.showContactUsError\n\n  def showCollectionError(): TaskService[Unit] =\n    actions.showMessageCollectionError\n\n  def showGenericError(): TaskService[Unit] = actions.showContactUsError\n\n}\n\ncase class PublishCollectionStatuses(collection: Option[Collection] = None)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/dialog/publishcollection/PublishCollectionStyles.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.dialog.publishcollection\n\nimport android.view.{View, ViewGroup}\nimport android.widget.TextView\nimport cards.nine.app.ui.commons.styles.CommonStyles\nimport cards.nine.app.ui.components.widgets.TintableImageView\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.{CardBackgroundColor, DrawerIconColor}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.LinearLayoutTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid.{ContextWrapper, Tweak}\n\ntrait PublishCollectionStyles extends CommonStyles {\n\n  def dialogBackgroundStyle(implicit context: ContextWrapper, theme: NineCardsTheme): Tweak[View] =\n    vBackgroundColor(theme.get(CardBackgroundColor))\n\n  def iconStyle(\n      alpha: Float = 1f)(implicit context: ContextWrapper, theme: NineCardsTheme): Tweak[View] =\n    vBackgroundColor(theme.get(DrawerIconColor).alpha(alpha))\n\n  def paginationItemStyle(implicit context: ContextWrapper): Tweak[TintableImageView] = {\n    val size   = resGetDimensionPixelSize(R.dimen.publish_collection_size_pager)\n    val margin = resGetDimensionPixelSize(R.dimen.publish_collection_margin_pager)\n    lp[ViewGroup](size, size) +\n      llLayoutMargin(margin, margin, margin, margin) +\n      ivSrc(R.drawable.publish_collection_wizard_pager)\n  }\n\n  def spinnerStyle(implicit context: ContextWrapper, theme: NineCardsTheme): Tweak[TextView] =\n    titleTextStyle +\n      tvSizeResource(R.dimen.text_xlarge) +\n      tvColor(theme.get(DrawerIconColor).alpha(0.3f))\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/jobs/GroupCollectionsJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.jobs\n\nimport android.content.Intent\nimport cards.nine.app.commons.{AppNineCardsIntentConversions, Conversions}\nimport cards.nine.app.ui.collections.CollectionsDetailsActivity._\nimport cards.nine.app.ui.collections.jobs.uiactions.{\n  GroupCollectionsUiActions,\n  NavigationUiActions,\n  ToolbarUiActions\n}\nimport cards.nine.app.ui.commons.action_filters.MomentReloadedActionFilter\nimport cards.nine.app.ui.commons._\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.Card._\nimport cards.nine.models.types._\nimport cards.nine.models.{Card, CardData, Collection}\nimport cats.implicits._\nimport macroid.ActivityContextWrapper\n\nclass GroupCollectionsJobs(\n    val groupCollectionsUiActions: GroupCollectionsUiActions,\n    val toolbarUiActions: ToolbarUiActions,\n    val navigationUiActions: NavigationUiActions)(\n    implicit activityContextWrapper: ActivityContextWrapper)\n    extends ShortcutJobs\n    with Conversions\n    with AppNineCardsIntentConversions { self =>\n\n  val delay = 200\n\n  var collections: Seq[Collection] = Seq.empty\n\n  def initialize(\n      backgroundColor: Int,\n      initialToolbarColor: Int,\n      icon: String,\n      position: Int,\n      isStateChanged: Boolean): TaskService[Unit] = {\n    for {\n      _           <- toolbarUiActions.initialize(backgroundColor, initialToolbarColor, icon, isStateChanged)\n      theme       <- getThemeTask\n      _           <- TaskService.right(statuses = statuses.copy(theme = theme))\n      _           <- groupCollectionsUiActions.initialize()\n      collections <- di.collectionProcess.getCollections\n      _           <- groupCollectionsUiActions.showCollections(collections, position)\n    } yield ()\n  }\n\n  def resume(): TaskService[Unit] = di.observerRegister.registerObserverTask()\n\n  def pause(): TaskService[Unit] = di.observerRegister.unregisterObserverTask()\n\n  def back(): TaskService[Unit] = groupCollectionsUiActions.back()\n\n  def destroy(): TaskService[Unit] = groupCollectionsUiActions.destroy()\n\n  def reloadCards(): TaskService[Seq[Card]] =\n    for {\n      currentCollection <- fetchCurrentCollection\n      databaseCollection <- di.collectionProcess\n        .getCollectionById(currentCollection.id)\n        .resolveOption(s\"Can't find the collection with id ${currentCollection.id}\")\n      cardsAreDifferent = databaseCollection.cards != currentCollection.cards\n      _ <- groupCollectionsUiActions\n        .reloadCards(databaseCollection.cards)\n        .resolveIf(cardsAreDifferent, ())\n      currentIsMoment <- collectionIsMoment(currentCollection.id)\n      _ <- sendBroadCastTask(BroadAction(MomentReloadedActionFilter.action))\n        .resolveIf(cardsAreDifferent && currentIsMoment, ())\n    } yield databaseCollection.cards\n\n  def editCard(): TaskService[Unit] =\n    for {\n      currentCollection <- fetchCurrentCollection\n      currentCollectionId = currentCollection.id\n      cards               = filterSelectedCards(currentCollection.cards)\n      _ <- cards match {\n        case head :: tail if tail.isEmpty =>\n          closeEditingMode() *> groupCollectionsUiActions\n            .editCard(currentCollectionId, head.id, head.term)\n        case _ =>\n          TaskService.left[Unit](JobException(\"You only can edit one card\"))\n      }\n    } yield ()\n\n  def removeCardsInEditMode(): TaskService[Seq[Card]] =\n    for {\n      currentCollection <- fetchCurrentCollection\n      cards = filterSelectedCards(currentCollection.cards)\n      _ <- closeEditingMode()\n      _ <- removeCards(currentCollection.id, cards)\n    } yield cards\n\n  def removeCardsByPackagesName(packageNames: Seq[String]): TaskService[Seq[Card]] =\n    for {\n      _                 <- di.trackEventProcess.removeAppsByFab(packageNames)\n      currentCollection <- fetchCurrentCollection\n      cards = packageNames flatMap (packageName =>\n                                      currentCollection.cards.find(\n                                        _.packageName == Option(packageName)))\n      _ <- removeCards(currentCollection.id, cards)\n    } yield cards\n\n  def removeCards(currentCollectionId: Int, cards: Seq[Card]) =\n    for {\n      _               <- di.trackEventProcess.removeApplications(cards flatMap (_.packageName))\n      _               <- di.collectionProcess.deleteCards(currentCollectionId, cards map (_.id))\n      _               <- groupCollectionsUiActions.removeCards(cards)\n      currentIsMoment <- collectionIsMoment(currentCollectionId)\n      _ <- sendBroadCastTask(BroadAction(MomentReloadedActionFilter.action))\n        .resolveIf(currentIsMoment, ())\n    } yield cards\n\n  def moveToCollection(toCollectionId: Int, collectionPosition: Int): TaskService[Seq[Card]] =\n    for {\n      currentCollection <- fetchCurrentCollection\n      _                 <- di.trackEventProcess.moveApplications(currentCollection.name)\n      toCollection <- groupCollectionsUiActions\n        .getCollection(collectionPosition)\n        .resolveOption(s\"Can't find the collection in the position $collectionPosition in the UI\")\n      currentCollectionId = currentCollection.id\n      cards               = filterSelectedCards(currentCollection.cards)\n      otherIsMoment       = toCollection.collectionType == MomentCollectionType\n      _               <- closeEditingMode()\n      _               <- di.collectionProcess.deleteCards(currentCollectionId, cards map (_.id))\n      _               <- di.collectionProcess.addCards(toCollectionId, cards map (_.toData))\n      _               <- groupCollectionsUiActions.removeCards(cards)\n      _               <- groupCollectionsUiActions.addCardsToCollection(collectionPosition, cards)\n      currentIsMoment <- collectionIsMoment(currentCollection.id)\n      _ <- sendBroadCastTask(BroadAction(MomentReloadedActionFilter.action))\n        .resolveIf(currentIsMoment || otherIsMoment, ())\n    } yield cards\n\n  def savePublishStatus(): TaskService[Unit] =\n    for {\n      currentCollection <- fetchCurrentCollection\n      _ <- TaskService.right(\n        statuses = statuses.copy(publishStatus = currentCollection.publicCollectionStatus))\n    } yield ()\n\n  def performCard(card: Card, position: Int): TaskService[Unit] = {\n\n    def sendTrackEvent() = {\n      val packageName     = card.packageName getOrElse \"\"\n      val maybeCollection = groupCollectionsUiActions.dom.getCurrentCollection\n\n      def trackMomentIfNecessary(collectionId: Option[Int]) =\n        collectionId match {\n          case Some(id) =>\n            for {\n              maybeMoment <- di.momentProcess.getMomentByCollectionId(id)\n              _ <- maybeMoment match {\n                case Some(moment) =>\n                  di.trackEventProcess\n                    .openAppFromCollection(packageName, MomentCategory(moment.momentType))\n                case _ => TaskService.empty\n              }\n            } yield ()\n          case _ => TaskService.empty\n        }\n\n      for {\n        _ <- maybeCollection flatMap (_.appsCategory) match {\n          case Some(category) =>\n            di.trackEventProcess.openAppFromCollection(packageName, AppCategory(category))\n          case _ => TaskService.empty\n        }\n        _ <- trackMomentIfNecessary(maybeCollection.map(_.id))\n      } yield ()\n    }\n\n    statuses.collectionMode match {\n      case EditingCollectionMode =>\n        val positions = if (statuses.positionsEditing.contains(position)) {\n          statuses.positionsEditing - position\n        } else {\n          statuses.positionsEditing + position\n        }\n        statuses = statuses.copy(positionsEditing = positions)\n        if (statuses.positionsEditing.isEmpty) {\n          closeEditingMode()\n        } else {\n          groupCollectionsUiActions.reloadItemCollection(statuses.getPositionsSelected, position)\n        }\n      case NormalCollectionMode =>\n        di.launcherExecutorProcess.execute(card.intent) *> sendTrackEvent()\n\n    }\n  }\n\n  def requestCallPhonePermission(phone: Option[String]): TaskService[Unit] = {\n    statuses = statuses.copy(lastPhone = phone)\n    di.userAccountsProcess.requestPermission(RequestCodes.phoneCallPermission, CallPhone)\n  }\n\n  def requestPermissionsResult(\n      requestCode: Int,\n      permissions: Array[String],\n      grantResults: Array[Int]): TaskService[Unit] =\n    if (requestCode == RequestCodes.phoneCallPermission) {\n      for {\n        result <- di.userAccountsProcess.parsePermissionsRequestResult(permissions, grantResults)\n        hasCallPhonePermission = result.exists(_.hasPermission(CallPhone))\n        _ <- (hasCallPhonePermission, statuses.lastPhone) match {\n          case (true, Some(phone)) =>\n            di.launcherExecutorProcess.execute(phoneToNineCardIntent(None, phone))\n          case (false, Some(phone)) =>\n            di.launcherExecutorProcess.launchDial(Some(phone)) *>\n              groupCollectionsUiActions.showNoPhoneCallPermissionError()\n          case _ => TaskService.empty\n        }\n        _ <- TaskService.right(statuses = statuses.copy(lastPhone = None))\n      } yield ()\n    } else {\n      TaskService.empty\n    }\n\n  def addCards(cardsRequest: Seq[CardData]): TaskService[Seq[Card]] =\n    for {\n      _                 <- di.trackEventProcess.addAppsByFab(cardsRequest flatMap (_.packageName))\n      currentCollection <- fetchCurrentCollection\n      currentCollectionId = currentCollection.id\n      cards           <- di.collectionProcess.addCards(currentCollectionId, cardsRequest)\n      _               <- groupCollectionsUiActions.addCards(cards)\n      currentIsMoment <- collectionIsMoment(currentCollection.id)\n      _ <- sendBroadCastTask(BroadAction(MomentReloadedActionFilter.action))\n        .resolveIf(currentIsMoment, ())\n    } yield cards\n\n  def addShortcut(intent: Intent): TaskService[Option[Card]] = {\n\n    def shortcutAdded(collectionId: Int, card: Card): TaskService[Unit] =\n      for {\n        _               <- di.trackEventProcess.addShortcutByFab(card.term)\n        _               <- groupCollectionsUiActions.addCards(Seq(card))\n        currentIsMoment <- collectionIsMoment(collectionId)\n        _ <- sendBroadCastTask(BroadAction(MomentReloadedActionFilter.action))\n          .resolveIf(currentIsMoment, ())\n      } yield ()\n\n    for {\n      currentCollection <- fetchCurrentCollection\n      maybeCard         <- addNewShortcut(currentCollection.id, intent)\n      _ <- maybeCard match {\n        case Some(card) => shortcutAdded(currentCollection.id, card)\n        case _          => TaskService.empty\n      }\n    } yield maybeCard\n  }\n\n  def openReorderMode(): TaskService[Unit] =\n    for {\n      _ <- statuses.collectionMode match {\n        case EditingCollectionMode =>\n          groupCollectionsUiActions.closeEditingModeUi()\n        case _ =>\n          TaskService.right(statuses = statuses.copy(collectionMode = EditingCollectionMode))\n      }\n      _ <- groupCollectionsUiActions.openReorderModeUi()\n    } yield ()\n\n  def closeReorderMode(position: Int): TaskService[Unit] = {\n    statuses = statuses.copy(positionsEditing = Set(position))\n    groupCollectionsUiActions.startEditing(statuses.getPositionsSelected)\n  }\n\n  def closeEditingMode(): TaskService[Unit] = {\n    statuses = statuses.copy(collectionMode = NormalCollectionMode, positionsEditing = Set.empty)\n    groupCollectionsUiActions.closeEditingModeUi()\n  }\n\n  def emptyCollection(): TaskService[Unit] =\n    for {\n      currentCollection <- fetchCurrentCollection\n      _ <- groupCollectionsUiActions\n        .showMenu(autoHide = false, indexColor = currentCollection.themedColorIndex)\n    } yield ()\n\n  def firstItemInCollection(): TaskService[Unit] =\n    groupCollectionsUiActions.hideMenuButton()\n\n  def close(): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.closeCollectionByGesture()\n      _ <- groupCollectionsUiActions.close()\n    } yield ()\n\n  def showMenu(openMenu: Boolean = false): TaskService[Unit] =\n    for {\n      _                 <- di.trackEventProcess.addCardByMenu()\n      currentCollection <- fetchCurrentCollection\n      _ <- groupCollectionsUiActions\n        .showMenu(autoHide = true, openMenu = openMenu, currentCollection.themedColorIndex)\n    } yield ()\n\n  private[this] def filterSelectedCards(cards: Seq[Card]): Seq[Card] =\n    cards.zipWithIndex flatMap {\n      case (card, index) if statuses.positionsEditing.contains(index) =>\n        Option(card)\n      case _ => None\n    }\n\n  private[this] def fetchCurrentCollection: TaskService[Collection] =\n    groupCollectionsUiActions.getCurrentCollection.resolveOption(\n      \"Can't find the current collection in the UI\")\n\n  private[this] def collectionIsMoment(currentCollectionId: Int): TaskService[Boolean] =\n    for {\n      // TODO Create getMomentByCollectionId #975\n      moments <- di.momentProcess.getMoments\n      currentIsMoment = moments.exists(_.collectionId.contains(currentCollectionId))\n    } yield currentIsMoment\n\n}\n\nsealed trait CollectionMode\n\ncase object NormalCollectionMode extends CollectionMode\n\ncase object EditingCollectionMode extends CollectionMode\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/jobs/NavigationJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.jobs\n\nimport android.os.Bundle\nimport cards.nine.app.ui.collections.CollectionsDetailsActivity._\nimport cards.nine.app.ui.commons.dialogs.apps.AppsFragment\nimport cards.nine.app.ui.commons.dialogs.recommendations.RecommendationsFragment\nimport cards.nine.app.ui.collections.jobs.uiactions.{\n  GroupCollectionsUiActions,\n  NavigationUiActions\n}\nimport cards.nine.app.ui.commons.{JobException, Jobs}\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.NineCardsCategory\nimport macroid.ActivityContextWrapper\n\nclass NavigationJobs(\n    val groupCollectionsUiActions: GroupCollectionsUiActions,\n    val navigationUiActions: NavigationUiActions)(\n    implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs {\n\n  def showAppDialog()(\n      implicit groupCollectionsJobs: GroupCollectionsJobs,\n      singleCollectionJobs: Option[SingleCollectionJobs]): TaskService[Unit] = {\n    val collection = navigationUiActions.dom.getCurrentCollection\n    val category   = collection flatMap (_.appsCategory)\n    val map        = category map (cat => Map(AppsFragment.categoryKey -> cat)) getOrElse Map.empty\n    val packages =\n      (collection map (_.cards flatMap (_.packageName))).toSeq.flatten\n    val args = createBundle(map, packages)\n    navigationUiActions.openApps(args)\n  }\n\n  def showRecommendationDialog()(\n      implicit groupCollectionsJobs: GroupCollectionsJobs,\n      singleCollectionJobs: Option[SingleCollectionJobs]): TaskService[Unit] = {\n    val collection = navigationUiActions.dom.getCurrentCollection\n    val packages   = collection map (_.cards flatMap (_.packageName)) getOrElse Seq.empty\n    val category   = collection flatMap (_.appsCategory)\n    val map = category map (cat =>\n                              Map(RecommendationsFragment.categoryKey -> cat)) getOrElse Map.empty\n    if (category.isEmpty && packages.isEmpty) {\n      groupCollectionsUiActions.showContactUsError()\n    } else {\n      val args = createBundle(map)\n      navigationUiActions.openRecommendations(args)\n    }\n  }\n\n  def showContactsDialog()(\n      implicit groupCollectionsJobs: GroupCollectionsJobs,\n      singleCollectionJobs: Option[SingleCollectionJobs]): TaskService[Unit] = {\n    val args = createBundle()\n    navigationUiActions.openContacts(args)\n  }\n\n  def showShortcutDialog()(\n      implicit groupCollectionsJobs: GroupCollectionsJobs,\n      singleCollectionJobs: Option[SingleCollectionJobs]): TaskService[Unit] =\n    for {\n      _ <- singleCollectionJobs match {\n        case Some(job) => job.saveCollectionIdForShortcut()\n        case _         => TaskService.empty\n      }\n      args <- TaskService(CatchAll[JobException](createBundle()))\n      _    <- navigationUiActions.openShortcuts(args)\n    } yield ()\n\n  private[this] def createBundle(\n      map: Map[String, NineCardsCategory] = Map.empty,\n      packages: Seq[String] = Seq.empty): Bundle = {\n    val args = new Bundle()\n    args.putStringArray(BaseActionFragment.packages, packages.toArray)\n    map foreach (item => {\n                   val (categoryKey, category) = item\n                   args.putString(categoryKey, category.name)\n                 })\n    navigationUiActions.dom.getCurrentCollection foreach (c =>\n                                                            args.putInt(\n                                                              BaseActionFragment.colorPrimary,\n                                                              statuses.theme.getIndexColor(\n                                                                c.themedColorIndex)))\n    args\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/jobs/SharedCollectionJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.jobs\n\nimport cards.nine.app.commons.{AppNineCardsIntentConversions, Conversions}\nimport cards.nine.app.ui.collections.jobs.uiactions.SharedCollectionUiActions\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.app.ui.commons.ops.CollectionOps._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.models.Collection\nimport cards.nine.models.types.AppCardType\nimport macroid.ActivityContextWrapper\n\nclass SharedCollectionJobs(val actions: SharedCollectionUiActions)(\n    implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with Conversions\n    with AppNineCardsIntentConversions { self =>\n\n  def reloadSharedCollectionId(): TaskService[Unit] =\n    for {\n      currentCollection <- fetchCurrentCollection\n      databaseCollection <- di.collectionProcess\n        .getCollectionById(currentCollection.id)\n        .resolveOption(s\"Can't find the collection with id ${currentCollection.id}\")\n      areDifferentCollections = databaseCollection.sharedCollectionId != currentCollection.sharedCollectionId\n      _ <- actions\n        .reloadSharedCollectionId(databaseCollection.sharedCollectionId)\n        .resolveIf(areDifferentCollections, (): Unit)\n    } yield (): Unit\n\n  def showPublishCollectionWizard(): TaskService[Unit] =\n    for {\n      currentCollection <- fetchCurrentCollection\n      _ <- if (currentCollection.cards.exists(_.cardType == AppCardType)) {\n        actions.showPublishCollectionWizardDialog(currentCollection)\n      } else {\n        actions.showMessagePublishContactsCollectionError\n      }\n    } yield (): Unit\n\n  def shareCollection(): TaskService[Unit] = {\n\n    def launchShareCollection(sharedCollectionId: String, url: String): TaskService[Unit] =\n      for {\n        _ <- di.trackEventProcess.shareCollectionByMenu(sharedCollectionId)\n        _ <- di.launcherExecutorProcess.launchShare(url)\n      } yield ()\n\n    for {\n      currentCollection <- fetchCurrentCollection\n      databaseCollection <- di.collectionProcess\n        .getCollectionById(currentCollection.id)\n        .resolveOption(s\"Can't find the collection with id ${currentCollection.id}\")\n      _ <- (databaseCollection.sharedCollectionId, databaseCollection.getUrlSharedCollection) match {\n        case (Some(sharedCollectionId), Some(url)) =>\n          launchShareCollection(sharedCollectionId, url)\n        case _ => actions.showMessageNotPublishedCollectionError\n      }\n    } yield (): Unit\n  }\n\n  private[this] def fetchCurrentCollection: TaskService[Collection] =\n    actions.getCurrentCollection.resolveOption(\"Can't find the current collection in the UI\")\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/jobs/SingleCollectionJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.jobs\n\nimport android.content.Context\nimport android.support.v7.widget.RecyclerView.ViewHolder\nimport cards.nine.app.commons.{AppNineCardsIntentConversions, Conversions}\nimport cards.nine.app.receivers.shortcuts.ShortcutBroadcastReceiver._\nimport cards.nine.app.ui.collections.jobs.uiactions.SingleCollectionUiActions\nimport cards.nine.app.ui.commons.{JobException, Jobs}\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types._\nimport cards.nine.models.{Card, Collection}\nimport cats.syntax.either._\nimport macroid.ActivityContextWrapper\nimport monix.eval.Task\n\nclass SingleCollectionJobs(\n    animateCards: Boolean,\n    maybeCollection: Option[Collection],\n    val actions: SingleCollectionUiActions)(\n    implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with Conversions\n    with AppNineCardsIntentConversions { self =>\n\n  lazy val preferences =\n    contextSupport.context.getSharedPreferences(shortcutBroadcastPreferences, Context.MODE_PRIVATE)\n\n  def initialize(): TaskService[Unit] = {\n    for {\n      theme <- getThemeTask\n      _ <- maybeCollection match {\n        case Some(collection) => actions.initialize(animateCards, collection)\n        case _                => actions.showEmptyCollection()\n      }\n    } yield ()\n  }\n\n  def startReorderCards(holder: ViewHolder): TaskService[Unit] =\n    for {\n      pulling <- actions.isToolbarPulling\n      _       <- actions.startReorder(holder).resolveIf(!pulling, ())\n    } yield ()\n\n  def reorderCard(collectionId: Int, cardId: Int, position: Int): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.reorderApplication(position)\n      _ <- di.collectionProcess.reorderCard(collectionId, cardId, position)\n      _ <- actions.reloadCards()\n    } yield ()\n\n  def moveToCollection(): TaskService[Unit] =\n    for {\n      collections <- di.collectionProcess.getCollections\n      _           <- actions.moveToCollection(collections)\n    } yield ()\n\n  def addCards(cards: Seq[Card]): TaskService[Unit] =\n    for {\n      _ <- trackCards(cards, AddedToCollectionAction)\n      _ <- actions.addCards(cards)\n    } yield ()\n\n  def removeCards(cards: Seq[Card]): TaskService[Unit] =\n    for {\n      _ <- trackCards(cards, RemovedFromCollectionAction)\n      _ <- actions.removeCards(cards)\n    } yield ()\n\n  def reloadCards(cards: Seq[Card]): TaskService[Unit] =\n    actions.reloadCards(cards)\n\n  def bindAnimatedAdapter(): TaskService[Unit] = maybeCollection match {\n    case Some(collection) =>\n      actions.bindAnimatedAdapter(animateCards, collection)\n    case _ => TaskService.left(JobException(\"Collection not found\"))\n  }\n\n  def saveEditedCard(collectionId: Int, cardId: Int, cardName: Option[String]): TaskService[Unit] =\n    cardName match {\n      case Some(name) if name.length > 0 =>\n        for {\n          card <- di.collectionProcess.editCard(collectionId, cardId, name)\n          _    <- actions.reloadCard(card)\n        } yield ()\n      case _ => actions.showMessageFormFieldError\n    }\n\n  def showData(): TaskService[Unit] = maybeCollection match {\n    case Some(collection) => actions.showData(collection.cards.isEmpty)\n    case _                => TaskService.left(JobException(\"Collection not found\"))\n  }\n\n  def showGenericError(): TaskService[Unit] = actions.showContactUsError()\n\n  def saveCollectionIdForShortcut(): TaskService[Unit] =\n    for {\n      collection <- actions.getCurrentCollection.resolveOption(\n        \"Can't find the current collection in the UI\")\n      _ <- TaskService(\n        CatchAll[JobException](preferences.edit().putInt(collectionIdKey, collection.id).apply()))\n    } yield ()\n\n  def removeCollectionIdForShortcut(): TaskService[Unit] = TaskService {\n    CatchAll[JobException](preferences.edit().remove(collectionIdKey).apply())\n  }\n\n  private[this] def trackCards(cards: Seq[Card], action: Action): TaskService[Unit] =\n    TaskService {\n      val tasks = cards map { card =>\n        trackCard(card, action).value\n      }\n      Task.gatherUnordered(tasks) map (_ => Either.right(()))\n    }\n\n  private[this] def trackCard(card: Card, action: Action): TaskService[Unit] =\n    card.cardType match {\n      case AppCardType =>\n        for {\n          collection <- actions.getCurrentCollection.resolveOption(\n            \"Can't find the current collection in the UI\")\n          maybeCategory = collection.appsCategory map (c => Option(AppCategory(c))) getOrElse {\n            collection.moment map (moment => MomentCategory(moment.momentType))\n          }\n          _ <- (action, card.packageName, maybeCategory) match {\n            case (AddedToCollectionAction, Some(packageName), Some(category)) =>\n              di.trackEventProcess.addAppToCollection(packageName, category)\n            case (RemovedFromCollectionAction, Some(packageName), Some(category)) =>\n              di.trackEventProcess.removeFromCollection(packageName, category)\n            case _ => TaskService.empty\n          }\n        } yield ()\n      case _ => TaskService.empty\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/jobs/ToolbarJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.jobs\n\nimport cards.nine.app.commons.{AppNineCardsIntentConversions, Conversions}\nimport cards.nine.app.ui.collections.jobs.uiactions.ToolbarUiActions\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.commons.services.TaskService.TaskService\nimport macroid.ActivityContextWrapper\n\nclass ToolbarJobs(actions: ToolbarUiActions)(\n    implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with Conversions\n    with AppNineCardsIntentConversions { self =>\n\n  def pullToClose(scroll: Int, close: Boolean): TaskService[Unit] =\n    actions.pullCloseScrollY(scroll, close)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/jobs/uiactions/GroupCollectionsDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.jobs.uiactions\n\nimport android.app.Activity\nimport android.os.Bundle\nimport android.support.v4.app.FragmentActivity\nimport android.view.View\nimport cards.nine.app.ui.collections.{CollectionAdapter, CollectionsPagerAdapter}\nimport cards.nine.app.ui.commons.ActivityFindViews\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.models.{Card, CardData, Collection}\nimport com.fortysevendeg.ninecardslauncher.TR\nimport macroid.ActivityContextWrapper\n\nclass GroupCollectionsDOM(activity: Activity) {\n\n  import ActivityFindViews._\n\n  val fabButtonItem = \"fab_button\"\n\n  val opened = \"opened\"\n\n  val autoHideKey = \"autoHide\"\n\n  lazy val toolbar = findView(TR.collections_toolbar).run(activity)\n\n  lazy val toolbarTitle = findView(TR.collections_toolbar_title).run(activity)\n\n  lazy val root = findView(TR.collections_root).run(activity)\n\n  lazy val viewPager = findView(TR.collections_view_pager).run(activity)\n\n  lazy val tabs = findView(TR.collections_tabs).run(activity)\n\n  lazy val iconContent = findView(TR.collections_icon_content).run(activity)\n\n  lazy val icon = findView(TR.collections_icon).run(activity)\n\n  lazy val fabButton = findView(TR.fab_button).run(activity)\n\n  lazy val fabMenuContent = findView(TR.fab_menu_content).run(activity)\n\n  lazy val fabMenu = findView(TR.fab_menu).run(activity)\n\n  def isFabButtonVisible: Boolean = fabButton.getVisibility == View.VISIBLE\n\n  def isAutoHide: Boolean =\n    fabButton.getField[Boolean](autoHideKey) getOrElse false\n\n  def isMenuOpened: Boolean =\n    fabButton.getField[Boolean](opened) getOrElse false\n\n  def getCurrentPosition: Option[Int] =\n    getAdapter flatMap (_.getCurrentFragmentPosition)\n\n  def getCurrentCollection: Option[Collection] = getAdapter flatMap { adapter =>\n    adapter.getCurrentFragmentPosition flatMap adapter.collections.lift\n  }\n\n  def getCollection(position: Int): Option[Collection] =\n    getAdapter flatMap (_.collections.lift(position))\n\n  def getAdapter: Option[CollectionsPagerAdapter] =\n    viewPager.getAdapter match {\n      case adapter: CollectionsPagerAdapter => Some(adapter)\n      case _                                => None\n    }\n\n  def getActiveCollectionAdapter: Option[CollectionAdapter] =\n    for {\n      adapter           <- getAdapter\n      fragment          <- adapter.getActiveFragment\n      collectionAdapter <- fragment.getAdapter\n    } yield collectionAdapter\n\n  def notifyItemChangedCollectionAdapter(position: Int): Unit =\n    getActiveCollectionAdapter foreach (_.notifyItemChanged(position))\n\n  def notifyDataSetChangedCollectionAdapter(): Unit =\n    getActiveCollectionAdapter foreach (_.notifyDataSetChanged())\n\n  def invalidateOptionMenu(implicit activityContextWrapper: ActivityContextWrapper): Unit = {\n    activityContextWrapper.original.get match {\n      case Some(activity: FragmentActivity) =>\n        activity.supportInvalidateOptionsMenu()\n      case _ =>\n    }\n  }\n\n}\n\ntrait GroupCollectionsUiListener {\n\n  def closeEditingMode(): Unit\n\n  def isNormalMode: Boolean\n\n  def isEditingMode: Boolean\n\n  def showPublicCollectionDialog(collection: Collection): Unit\n\n  def showEditCollectionDialog(cardName: String, onChangeName: (Option[String]) => Unit): Unit\n\n  def addCards(cards: Seq[CardData]): Unit\n\n  def bindAnimatedAdapter(): Unit\n\n  def reloadCards(cards: Seq[Card]): Unit\n\n  def saveEditedCard(collectionId: Int, cardId: Int, cardName: Option[String]): Unit\n\n  def showDataInPosition(position: Int): Unit\n\n  def showAppsDialog(): Unit\n\n  def showContactsDialog(): Unit\n\n  def showShortcutsDialog(): Unit\n\n  def showRecommendationsDialog(): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/jobs/uiactions/GroupCollectionsUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.jobs.uiactions\n\nimport android.animation.ValueAnimator\nimport android.os.Handler\nimport android.support.design.widget.FloatingActionButton\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.support.v4.view.ViewPager\nimport android.support.v4.view.ViewPager.OnPageChangeListener\nimport android.view.Gravity\nimport android.view.ViewGroup.LayoutParams._\nimport android.widget.LinearLayout\nimport cards.nine.app.ui.collections.CollectionsDetailsActivity._\nimport cards.nine.app.ui.collections.CollectionsPagerAdapter\nimport cards.nine.app.ui.collections.snails.CollectionsSnails._\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.commons.dialogs.wizard.{CollectionsWizardInline, WizardInlinePreferences}\nimport cards.nine.app.ui.commons.ops.CollectionOps._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.drawables.tweaks.PathMorphDrawableTweaks._\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.app.ui.components.layouts.tweaks.FabItemMenuTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.SlidingTabLayoutTweaks._\nimport cards.nine.app.ui.components.layouts.{FabItemMenu, SlidingTabLayout}\nimport cards.nine.commons._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.theme.{\n  CollectionDetailTextTabDefaultColor,\n  CollectionDetailTextTabSelectedColor\n}\nimport cards.nine.models.{Card, Collection, NineCardsTheme}\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.FloatingActionButtonTweaks._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.UIActionsExtras._\nimport macroid.extras.ViewPagerTweaks._\nimport macroid.extras.ViewTweaks._\n\nimport scala.concurrent.ExecutionContext.Implicits.global\n\nclass GroupCollectionsUiActions(\n    val dom: GroupCollectionsDOM,\n    listener: GroupCollectionsUiListener)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_])\n    extends ImplicitsUiExceptions {\n\n  implicit lazy val systemBarsTint = new SystemBarsTint\n\n  lazy val wizardInlinePreferences = new WizardInlinePreferences()\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  // Ui Actions\n\n  def initialize(): TaskService[Unit] =\n    ((dom.tabs <~ tabsStyle) ~\n      initFabButton ~\n      loadMenuItems(getItemsForFabMenu)).toService()\n\n  def showCollections(collections: Seq[Collection], position: Int): TaskService[Unit] =\n    (collections lift position match {\n      case Some(collection) =>\n        val adapter =\n          CollectionsPagerAdapter(fragmentManagerContext.manager, collections, position)\n        (dom.viewPager <~ vpAdapter(adapter)) ~\n          Ui(adapter.activateFragment(position)) ~\n          (dom.tabs <~\n            stlViewPager(dom.viewPager) <~\n            stlOnPageChangeListener(new OnPageChangeCollectionsListener(\n              position,\n              updateToolbarColor,\n              updateCollection))) ~\n          uiHandler(dom.viewPager <~ vpCurrentItem(position, smoothScroll = false)) ~\n          uiHandlerDelayed(Ui {\n            listener.bindAnimatedAdapter()\n          }, delayMilis = 100) ~\n          (dom.tabs <~ vVisible <~~ enterViews)\n      case _ => Ui.nop\n    }).toService()\n\n  def openCollectionsWizardInline(): TaskService[Unit] =\n    (if (wizardInlinePreferences.shouldBeShowed(CollectionsWizardInline)) {\n       dom.root <~ vLauncherWizardSnackbar(\n         CollectionsWizardInline,\n         forceNavigationBarHeight = false)\n     } else {\n       Ui.nop\n     }).toService()\n\n  def showContactUsError(): TaskService[Unit] = showError().toService()\n\n  def back(): TaskService[Unit] =\n    (if (dom.isMenuOpened) {\n       swapFabMenu()\n     } else if (listener.isEditingMode) {\n       Ui(listener.closeEditingMode())\n     } else {\n       exitTransition\n     }).toService()\n\n  def destroy(): TaskService[Unit] =\n    Ui {\n      dom.getAdapter foreach (_.clear())\n    }.toService()\n\n  def getCurrentCollection: TaskService[Option[Collection]] = TaskService {\n    CatchAll[UiException](dom.getCurrentCollection)\n  }\n\n  def getCollection(position: Int): TaskService[Option[Collection]] =\n    TaskService {\n      CatchAll[UiException](dom.getCollection(position))\n    }\n\n  def reloadCards(cards: Seq[Card]): TaskService[Unit] =\n    Ui {\n      for {\n        adapter         <- dom.getAdapter\n        currentPosition <- adapter.getCurrentFragmentPosition\n      } yield {\n        adapter.updateCardFromCollection(currentPosition, cards)\n      }\n    }.toService()\n\n  def editCard(collectionId: Int, cardId: Int, cardName: String): TaskService[Unit] =\n    Ui(listener.showEditCollectionDialog(cardName, (maybeNewName) => {\n      listener.saveEditedCard(collectionId, cardId, maybeNewName)\n    })).toService()\n\n  def removeCards(cards: Seq[Card]): TaskService[Unit] =\n    Ui {\n      for {\n        adapter         <- dom.getAdapter\n        currentPosition <- adapter.getCurrentFragmentPosition\n      } yield {\n        adapter.removeCardFromCollection(currentPosition, cards)\n      }\n    }.toService()\n\n  def addCardsToCollection(collectionPosition: Int, cards: Seq[Card]): TaskService[Unit] =\n    Ui {\n      for {\n        adapter <- dom.getAdapter\n      } yield {\n        adapter.addCardsToCollection(collectionPosition, cards)\n        adapter.getFragmentByPosition(collectionPosition).foreach { fragment =>\n          fragment.getAdapter foreach (_.addCards(cards))\n          listener.showDataInPosition(collectionPosition)\n        }\n      }\n    }.toService()\n\n  def reloadItemCollection(itemsSelected: Int, position: Int): TaskService[Unit] =\n    (Ui(dom.invalidateOptionMenu) ~\n      (dom.toolbarTitle <~ tvText(resGetString(R.string.itemsSelected, itemsSelected.toString))) ~\n      Ui(dom.notifyItemChangedCollectionAdapter(position))).toService()\n\n  def showNoPhoneCallPermissionError(): TaskService[Unit] =\n    showMessage(R.string.noPhoneCallPermissionMessage).toService()\n\n  def addCards(cards: Seq[Card]): TaskService[Unit] =\n    Ui {\n      for {\n        adapter         <- dom.getAdapter\n        currentPosition <- adapter.getCurrentFragmentPosition\n      } yield {\n        adapter.addCardsToCollection(currentPosition, cards)\n      }\n    }.toService()\n\n  def openReorderModeUi(): TaskService[Unit] = hideFabButton.toService()\n\n  def startEditing(items: Int): TaskService[Unit] =\n    (Ui(dom.invalidateOptionMenu) ~\n      (dom.toolbarTitle <~ tvText(resGetString(R.string.itemsSelected, items.toString))) ~\n      (dom.iconContent <~ applyAnimation(alpha = Some(0))) ~\n      Ui(dom.notifyDataSetChangedCollectionAdapter())).toService()\n\n  def closeEditingModeUi(): TaskService[Unit] =\n    ((dom.toolbarTitle <~ tvText(\"\")) ~\n      (dom.iconContent <~ (vVisible + vScaleX(1) + vScaleY(1) + vAlpha(0f) ++ applyAnimation(\n        alpha = Some(1)))) ~\n      Ui(dom.notifyDataSetChangedCollectionAdapter()) ~\n      Ui(dom.invalidateOptionMenu)).toService()\n\n  def showMenu(\n      autoHide: Boolean = true,\n      openMenu: Boolean = false,\n      indexColor: Int): TaskService[Unit] = {\n    val color = theme.getIndexColor(indexColor)\n    (showFabButton(color, autoHide) ~\n      (if (openMenu) swapFabMenu(forceOpen = true) else Ui.nop)).toService()\n  }\n\n  def hideMenu(): TaskService[Unit] =\n    (if (dom.isFabButtonVisible) swapFabMenu() else Ui.nop).toService()\n\n  def hideMenuButton(): TaskService[Unit] = hideFabButton.toService()\n\n  def close(): TaskService[Unit] = exitTransition.toService()\n\n  // FabButtonBehaviour\n\n  private[this] def updateBarsInFabMenuHide(): Ui[Any] =\n    dom.getCurrentCollection map (c =>\n                                    systemBarsTint.updateStatusColor(\n                                      theme.getIndexColor(c.themedColorIndex))) getOrElse Ui.nop\n\n  private[this] var runnableHideFabButton: Option[RunnableWrapper] = None\n\n  private[this] lazy val handler = new Handler()\n\n  private[this] val timeDelayFabButton = 3000\n\n  private[this] def initFabButton: Ui[_] =\n    (dom.fabMenuContent <~ On.click(swapFabMenu()) <~ vClickable(false)) ~\n      (dom.fabButton <~ fabButtonMenuStyle <~ On.click(swapFabMenu()))\n\n  private[this] def loadMenuItems(items: Seq[FabItemMenu]): Ui[_] =\n    dom.fabMenu <~ Tweak[LinearLayout] { view =>\n      val param =\n        new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.END)\n      items foreach (view.addView(_, 0, param))\n    }\n\n  private[this] def swapFabMenu(\n      doUpdateBars: Boolean = true,\n      forceOpen: Boolean = false): Ui[Any] = {\n    val open     = if (forceOpen) false else dom.isMenuOpened\n    val autoHide = dom.isAutoHide\n    val ui = (dom.fabButton <~\n        vAddField(dom.opened, !open) <~\n        pmdAnimIcon(if (open) IconTypes.ADD else IconTypes.CLOSE)) ~\n        (dom.fabMenuContent <~\n          animFabButton(open) <~\n          colorContentDialog(!open) <~\n          vClickable(!open)) ~\n        (if (open && autoHide) postDelayedHideFabButton\n         else removeDelayedHideFabButton())\n    ui ~ (if (doUpdateBars) updateBars(open) else Ui.nop)\n  }\n\n  private[this] def colorContentDialog(paint: Boolean) =\n    vBackgroundColorResource(if (paint) R.color.background_dialog else android.R.color.transparent)\n\n  private[this] def showFabButton(color: Int = 0, autoHide: Boolean = true): Ui[_] =\n    if (dom.isFabButtonVisible && autoHide) {\n      resetDelayedHide\n    } else {\n      val colorDark = color.dark()\n      (if (autoHide) postDelayedHideFabButton\n       else removeDelayedHideFabButton()) ~\n        (dom.fabButton <~ (if (color != 0) fbaColor(color, colorDark)\n                           else\n                             Tweak.blank) <~ showFabMenu <~ vAddField(dom.autoHideKey, autoHide)) ~\n        (if (color != 0) dom.fabMenu <~ changeItemsColor(color) else Ui.nop)\n    }\n\n  def hideFabButton: Ui[_] =\n    removeDelayedHideFabButton() ~ (dom.fabButton <~ hideFabMenu)\n\n  def changeItemsColor(color: Int) = Transformer {\n    case item: FabItemMenu => item <~ fimBackgroundColor(color)\n  }\n\n  private[this] def updateBars(opened: Boolean): Ui[_] =\n    if (opened) {\n      updateBarsInFabMenuHide()\n    } else {\n      systemBarsTint.updateStatusToBlack()\n    }\n\n  private[this] def animFabButton(open: Boolean) = Transformer {\n    case i: FabItemMenu if i.isType(dom.fabButtonItem) =>\n      if (open) {\n        i <~ vGone\n      } else {\n        val position = i.getPosition\n        (i <~ animFabMenuItem(position)) ~\n          (i.icon <~ animFabMenuIconItem(position)) ~\n          (i.title <~ animFabMenuTitleItem(position))\n      }\n  }\n\n  private[this] def resetDelayedHide =\n    removeDelayedHideFabButton() ~ postDelayedHideFabButton\n\n  private[this] def postDelayedHideFabButton = Ui {\n    val runnable = new RunnableWrapper()\n    handler.postDelayed(runnable, timeDelayFabButton)\n    runnableHideFabButton = Option(runnable)\n  }\n\n  private[this] def removeDelayedHideFabButton() = Ui {\n    runnableHideFabButton foreach handler.removeCallbacks\n  }\n\n  class RunnableWrapper extends Runnable {\n    override def run(): Unit = (dom.fabButton <~ hideFabMenu).run\n  }\n\n  // Private utilities\n\n  private[this] def exitTransition: Ui[Any] = {\n    val activity = activityContextWrapper.getOriginal\n    ((dom.tabs <~ exitViews) ~\n      (dom.iconContent <~ exitViews)) ~\n      (dom.viewPager <~~ exitViews) ~~\n      Ui(activity.finish())\n  }\n\n  private[this] def getItemsForFabMenu = Seq(\n    (w[FabItemMenu] <~ fabButtonApplicationsStyle <~ On.click {\n      Ui(listener.showAppsDialog())\n    }).get,\n    (w[FabItemMenu] <~ fabButtonRecommendationsStyle <~ On.click {\n      Ui(listener.showRecommendationsDialog())\n    }).get,\n    (w[FabItemMenu] <~ fabButtonContactsStyle <~ On.click {\n      Ui(listener.showContactsDialog())\n    }).get,\n    (w[FabItemMenu] <~ fabButtonShortcutsStyle <~ On.click {\n      Ui(listener.showShortcutsDialog())\n    }).get\n  )\n\n  private[this] def showError(error: Int = R.string.contactUsError): Ui[Any] =\n    dom.root <~ vSnackbarShort(error)\n\n  private[this] def showMessage(message: Int): Ui[Any] = uiShortToast(message)\n\n  private[this] def updateToolbarColor(color: Int): Ui[Any] =\n    (dom.toolbar <~ vBackgroundColor(color)) ~\n      systemBarsTint.updateStatusColor(color)\n\n  private[this] def updateCollection(\n      collection: Collection,\n      position: Int,\n      pageMovement: PageMovement): Ui[Any] =\n    dom.getAdapter map { adapter =>\n      val resIcon = collection.getIconDetail\n      (pageMovement match {\n        case Start | Idle =>\n          dom.icon <~ ivSrc(resIcon)\n        case Left =>\n          dom.icon <~ animationIcon(fromLeft = true, resIcon)\n        case Right | Jump =>\n          dom.icon <~ animationIcon(fromLeft = false, resIcon)\n        case _ => Ui.nop\n      }) ~\n        adapter.notifyChanged(position) ~\n        (if (collection.cards.isEmpty) {\n           val color = theme.getIndexColor(collection.themedColorIndex)\n           showFabButton(color = color, autoHide = false)\n         } else {\n           hideFabButton\n         })\n    } getOrElse Ui.nop\n\n  // Styles\n\n  private[this] def tabsStyle: Tweak[SlidingTabLayout] =\n    stlDefaultTextColor(theme.get(CollectionDetailTextTabDefaultColor)) +\n      stlSelectedTextColor(theme.get(CollectionDetailTextTabSelectedColor)) +\n      vInvisible\n\n  private[this] def fabButtonApplicationsStyle: Tweak[FabItemMenu] =\n    fabButtonStyle(R.string.applications, R.drawable.fab_menu_icon_applications, 1)\n\n  private[this] def fabButtonRecommendationsStyle: Tweak[FabItemMenu] =\n    fabButtonStyle(R.string.recommendations, R.drawable.fab_menu_icon_recommendations, 2)\n\n  private[this] def fabButtonContactsStyle: Tweak[FabItemMenu] =\n    fabButtonStyle(R.string.contacts, R.drawable.fab_menu_icon_contact, 3)\n\n  private[this] def fabButtonShortcutsStyle: Tweak[FabItemMenu] =\n    fabButtonStyle(R.string.shortcuts, R.drawable.fab_menu_icon_shorcut, 4)\n\n  private[this] def fabButtonMenuStyle: Tweak[FloatingActionButton] = {\n    val iconFabButton = PathMorphDrawable(\n      defaultIcon = IconTypes.ADD,\n      defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_default))\n    ivSrc(iconFabButton) +\n      vAddField(dom.opened, false) +\n      vGone\n  }\n\n  private[this] def fabButtonStyle(title: Int, icon: Int, position: Int): Tweak[FabItemMenu] =\n    vWrapContent +\n      fimPopulate(resGetColor(R.color.collection_detail_fab_button_item), icon, title) +\n      vGone +\n      vSetType(dom.fabButtonItem) +\n      vSetPosition(position)\n\n  class OnPageChangeCollectionsListener(\n      position: Int,\n      updateToolbarColor: (Int) => Ui[Any],\n      updateCollection: (Collection, Int, PageMovement) => Ui[Any])\n      extends OnPageChangeListener {\n\n    var lastPosition = -1\n\n    var currentPosition = if (position == 0) position else -1\n\n    var currentMovement: PageMovement = if (position == 0) Left else Loading\n\n    private[this] def getColor(col: Collection): Int =\n      theme.getIndexColor(col.themedColorIndex)\n\n    private[this] def jump(from: Collection, to: Collection) = {\n      val valueAnimator = ValueAnimator.ofInt(0, 100)\n      valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n        override def onAnimationUpdate(value: ValueAnimator): Unit = {\n          val color = (getColor(from), getColor(to)).interpolateColors(value.getAnimatedFraction)\n          updateToolbarColor(color).run\n        }\n      })\n      valueAnimator.start()\n    }\n\n    override def onPageScrollStateChanged(state: Int): Unit = state match {\n      case ViewPager.SCROLL_STATE_IDLE     => currentMovement = Idle\n      case ViewPager.SCROLL_STATE_DRAGGING => listener.closeEditingMode()\n      case _                               =>\n    }\n\n    override def onPageScrolled(\n        position: Int,\n        positionOffset: Float,\n        positionOffsetPixels: Int): Unit =\n      currentMovement match {\n        case Loading => // Nothing\n        case Start => // First time, we change automatically the movement\n          currentMovement = if (currentPosition > 0) Jump else Idle\n        case Jump => // Nothing. The animation was triggered in onPageSelected\n        case _ => // Scrolling to left or right\n          for {\n            current <- dom.getCollection(position)\n            next    <- dom.getCollection(position + 1)\n          } yield {\n            val color = (getColor(current), getColor(next)).interpolateColors(positionOffset)\n            updateToolbarColor(color).run\n          }\n      }\n\n    override def onPageSelected(position: Int): Unit = {\n      val pageMovement: PageMovement = (position, currentPosition) match {\n        case (p, cp) if cp == -1             => Start\n        case (p, cp) if p > cp && p - cp > 1 => Jump\n        case (p, cp) if p < cp && cp - p > 1 => Jump\n        case (p, cp) if p < cp               => Left\n        case _                               => Right\n      }\n      lastPosition = currentPosition\n      currentPosition = position\n      currentMovement = pageMovement\n      pageMovement match {\n        case Jump =>\n          for {\n            last    <- dom.getCollection(lastPosition)\n            current <- dom.getCollection(currentPosition)\n          } yield jump(last, current)\n        case _ =>\n      }\n      dom.getCollection(position) foreach { collection =>\n        updateCollection(collection, position, pageMovement).run\n      }\n    }\n\n  }\n\n}\n\nsealed trait PageMovement\n\ncase object Loading extends PageMovement\n\ncase object Left extends PageMovement\n\ncase object Right extends PageMovement\n\ncase object Start extends PageMovement\n\ncase object Idle extends PageMovement\n\ncase object Jump extends PageMovement\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/jobs/uiactions/NavigationUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.jobs.uiactions\n\nimport android.os.Bundle\nimport android.support.v4.app.{DialogFragment, Fragment, FragmentManager}\nimport android.support.v7.app.{AppCompatActivity, AppCompatDialogFragment}\nimport cards.nine.app.ui.commons.dialogs.apps.AppsFragment\nimport cards.nine.app.ui.commons.dialogs.contacts.ContactsFragment\nimport cards.nine.app.ui.commons.dialogs.recommendations.RecommendationsFragment\nimport cards.nine.app.ui.commons.dialogs.shortcuts.ShortcutDialogFragment\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.collections.dialog.EditCardDialogFragment\nimport cards.nine.app.ui.collections.dialog.publishcollection.PublishCollectionFragment\nimport cards.nine.app.ui.collections.jobs.{\n  GroupCollectionsJobs,\n  SharedCollectionJobs,\n  SingleCollectionJobs\n}\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.commons._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.Collection\nimport macroid.{ActivityContextWrapper, FragmentManagerContext, Ui}\n\nclass NavigationUiActions(val dom: GroupCollectionsDOM)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_]) {\n\n  val tagDialog = \"dialog\"\n\n  def openApps(args: Bundle)(\n      implicit groupCollectionsJobs: GroupCollectionsJobs,\n      singleCollectionJobs: Option[SingleCollectionJobs]): TaskService[Unit] =\n    launchDialog(new AppsFragment, args).toService()\n\n  def openContacts(args: Bundle)(\n      implicit groupCollectionsJobs: GroupCollectionsJobs,\n      singleCollectionJobs: Option[SingleCollectionJobs]): TaskService[Unit] =\n    launchDialog(new ContactsFragment, args).toService()\n\n  def openShortcuts(args: Bundle)(\n      implicit groupCollectionsJobs: GroupCollectionsJobs,\n      singleCollectionJobs: Option[SingleCollectionJobs]): TaskService[Unit] =\n    launchDialog(new ShortcutDialogFragment, args).toService()\n\n  def openRecommendations(args: Bundle)(\n      implicit groupCollectionsJobs: GroupCollectionsJobs,\n      singleCollectionJobs: Option[SingleCollectionJobs]): TaskService[Unit] =\n    launchDialog(new RecommendationsFragment, args).toService()\n\n  def openPublishCollection(collection: Collection)(\n      implicit sharedCollectionJobs: SharedCollectionJobs): TaskService[Unit] =\n    TaskService.right(showDialog(PublishCollectionFragment(collection)))\n\n  def openEditCard(cardName: String, onChangeName: (Option[String]) => Unit): TaskService[Unit] =\n    TaskService.right(showDialog(new EditCardDialogFragment(cardName, onChangeName)))\n\n  private[this] def showDialog(dialog: DialogFragment): Unit = {\n    activityContextWrapper.original.get match {\n      case Some(activity: AppCompatActivity) =>\n        val ft = activity.getSupportFragmentManager.beginTransaction()\n        Option(activity.getSupportFragmentManager.findFragmentByTag(tagDialog)) foreach ft.remove\n        ft.addToBackStack(javaNull)\n        dialog.show(ft, tagDialog)\n      case _ =>\n    }\n  }\n\n  private[this] def launchDialog[F <: DialogFragment](fragment: F, args: Bundle): Ui[Any] =\n    Ui {\n      fragment.setArguments(args)\n      fragment.show(fragmentManagerContext.manager, tagDialog)\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/jobs/uiactions/SharedCollectionUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.jobs.uiactions\n\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.{ImplicitsUiExceptions, UiContext, UiException}\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.Collection\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\n\nclass SharedCollectionUiActions(\n    val dom: GroupCollectionsDOM,\n    listener: GroupCollectionsUiListener)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_])\n    extends ImplicitsUiExceptions {\n\n  def reloadSharedCollectionId(sharedCollectionId: Option[String]): TaskService[Unit] =\n    Ui {\n      for {\n        adapter         <- dom.getAdapter\n        currentPosition <- adapter.getCurrentFragmentPosition\n        _ = adapter.updateShareCollectionIdFromCollection(currentPosition, sharedCollectionId)\n      } yield dom.invalidateOptionMenu\n    }.toService()\n\n  def showPublishCollectionWizardDialog(collection: Collection): TaskService[Unit] =\n    Ui(listener.showPublicCollectionDialog(collection)).toService()\n\n  def showMessagePublishContactsCollectionError: TaskService[Unit] =\n    showError(R.string.publishCollectionError).toService()\n\n  def showMessageNotPublishedCollectionError: TaskService[Unit] =\n    showError(R.string.notPublishedCollectionError).toService()\n\n  def getCurrentCollection: TaskService[Option[Collection]] = TaskService {\n    CatchAll[UiException](dom.getCurrentCollection)\n  }\n\n  private[this] def showError(error: Int = R.string.contactUsError): Ui[Any] =\n    dom.root <~ vSnackbarShort(error)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/jobs/uiactions/SingleCollectionDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.jobs.uiactions\n\nimport android.support.v7.widget.RecyclerView.ViewHolder\nimport cards.nine.app.ui.collections.CollectionAdapter\nimport cards.nine.app.ui.components.layouts.tweaks.PullToDownViewTweaks._\nimport cards.nine.models.{Card, Collection}\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\n\ntrait SingleCollectionDOM { self: TypedFindView =>\n\n  lazy val emptyCollectionView = findView(TR.collection_detail_empty)\n\n  lazy val emptyCollectionMessage = findView(TR.collection_empty_message)\n\n  lazy val recyclerView = findView(TR.collection_detail_recycler)\n\n  lazy val pullToCloseView = findView(TR.collection_detail_pull_to_close)\n\n  def getAdapter: Option[CollectionAdapter] = recyclerView.getAdapter match {\n    case a: CollectionAdapter => Some(a)\n    case _                    => None\n  }\n\n  def isPulling: Boolean = (pullToCloseView ~> pdvIsPulling()).get\n\n  def getCurrentCollection: Option[Collection] = getAdapter map (_.collection)\n\n}\n\ntrait SingleCollectionUiListener {\n\n  def reorderCard(collectionId: Int, cardId: Int, position: Int): Unit\n\n  def scrollStateChanged(idDragging: Boolean): Unit\n\n  def close(): Unit\n\n  def pullToClose(scroll: Int, close: Boolean): Unit\n\n  def reloadCards(): Unit\n\n  def moveToCollection(toCollectionId: Int, collectionPosition: Int): Unit\n\n  def firstItemInCollection(): Unit\n\n  def emptyCollection(): Unit\n\n  def openReorderMode(): Unit\n\n  def closeReorderMode(position: Int): Unit\n\n  def performCard(card: Card, position: Int): Unit\n\n  def startReorderCards(holder: ViewHolder): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/jobs/uiactions/SingleCollectionUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.jobs.uiactions\n\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.support.v7.widget.RecyclerView.ViewHolder\nimport android.support.v7.widget.helper.ItemTouchHelper\nimport android.support.v7.widget.{DefaultItemAnimator, GridLayoutManager, RecyclerView}\nimport android.text.Html\nimport cards.nine.app.ui.collections.CollectionAdapter\nimport cards.nine.app.ui.collections.CollectionsDetailsActivity._\nimport cards.nine.app.ui.commons.Constants._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.{ImplicitsUiExceptions, UiContext}\nimport cards.nine.app.ui.components.commons.{PaddingItemDecoration, _}\nimport cards.nine.app.ui.components.dialogs.CollectionDialog\nimport cards.nine.app.ui.components.layouts.tweaks.PullToCloseViewTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.PullToDownViewTweaks._\nimport cards.nine.app.ui.components.layouts.{PullToCloseListener, PullingListener}\nimport cards.nine.app.ui.components.widgets.tweaks.CollectionRecyclerViewTweaks._\nimport cards.nine.app.ui.preferences.commons.CardPadding\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.theme.{CardBackgroundColor, DrawerTextColor}\nimport cards.nine.models.{Card, Collection, NineCardsTheme}\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\nimport macroid.extras.CardViewTweaks._\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.UIActionsExtras._\nimport macroid.extras.ViewTweaks._\n\nimport scala.language.postfixOps\n\nclass SingleCollectionUiActions(\n    val dom: SingleCollectionDOM,\n    listener: SingleCollectionUiListener)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_])\n    extends ImplicitsUiExceptions {\n\n  case class SingleCollectionStatuses(\n      touchHelper: Option[ItemTouchHelper] = None,\n      activeFragment: Boolean = false)\n\n  val tagDialog = \"dialog\"\n\n  var singleCollectionStatuses = SingleCollectionStatuses()\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  lazy val messageText =\n    Html.fromHtml(resGetString(R.string.collectionDetailAddCardsMessage))\n\n  def startReorder(holder: ViewHolder): TaskService[Unit] =\n    (singleCollectionStatuses.touchHelper match {\n      case Some(th) => uiVibrate() ~ Ui(th.startDrag(holder))\n      case _        => Ui.nop\n    }).toService()\n\n  def initialize(animateCards: Boolean, collection: Collection): TaskService[Unit] = {\n    (Ui {\n      val itemTouchCallback = new ReorderItemTouchHelperCallback(onChanged = {\n        case (ActionStateReordering, position) =>\n          openReorderMode.run\n        case (ActionStateIdle, position) =>\n          for {\n            adapter <- dom.getAdapter\n            collection = adapter.collection\n            card <- collection.cards.lift(position)\n          } yield listener.reorderCard(collection.id, card.id, position)\n          closeReorderMode(position).run\n      })\n      val itemTouchHelper = new ItemTouchHelper(itemTouchCallback)\n      itemTouchHelper.attachToRecyclerView(dom.recyclerView)\n\n      singleCollectionStatuses = singleCollectionStatuses.copy(touchHelper = Some(itemTouchHelper))\n    } ~\n      (dom.recyclerView <~\n        vGlobalLayoutListener(view => {\n          loadCollection(collection, animateCards)\n        }) <~\n        rvAddOnScrollListener(scrolled = (_, _) => {}, scrollStateChanged = (newState) => {\n          val isDragging = newState == RecyclerView.SCROLL_STATE_DRAGGING\n          listener.scrollStateChanged(isDragging)\n        }) <~\n        (if (animateCards)\n           nrvEnableAnimation(R.anim.grid_cards_layout_animation)\n         else Tweak.blank)) ~\n      (dom.pullToCloseView <~\n        pcvListener(\n          PullToCloseListener(\n            close = () => listener.close()\n          )) <~\n        pdvPullingListener(\n          PullingListener(\n            start = () => (dom.recyclerView <~ nrvDisableScroll(true)).run,\n            end = () => (dom.recyclerView <~ nrvDisableScroll(false)).run,\n            scroll = (scroll: Int, close: Boolean) => listener.pullToClose(scroll, close)\n          )))).toService()\n  }\n\n  def reloadCards(): TaskService[Unit] = Ui(listener.reloadCards()).toService()\n\n  def isToolbarPulling: TaskService[Boolean] = TaskService.right(dom.isPulling)\n\n  def getCurrentCollection: TaskService[Option[Collection]] =\n    TaskService.right(dom.getCurrentCollection)\n\n  def showContactUsError(): TaskService[Unit] =\n    showMessage(R.string.contactUsError).toService()\n\n  def showMessageFormFieldError: TaskService[Unit] =\n    showMessage(R.string.formFieldError).toService()\n\n  def showEmptyCollection(): TaskService[Unit] =\n    ((dom.emptyCollectionMessage <~\n      tvText(messageText) <~\n      tvColor(statuses.theme.get(DrawerTextColor).alpha(0.8f))) ~\n      (dom.emptyCollectionView <~ vVisible <~ cvCardBackgroundColor(\n        statuses.theme.get(CardBackgroundColor))) ~\n      (dom.recyclerView <~ vGone)).toService()\n\n  def bindAnimatedAdapter(animateCards: Boolean, collection: Collection): TaskService[Unit] =\n    (dom.recyclerView <~\n      rvAdapter(createAdapter(collection)) <~\n      nrvScheduleLayoutAnimation).ifUi(animateCards).toService()\n\n  def moveToCollection(collections: Seq[Collection]): TaskService[Unit] =\n    Ui {\n      val momentDialog = new CollectionDialog(\n        collections filterNot (c => dom.getCurrentCollection.map(_.id).contains(c.id)),\n        c => listener.moveToCollection(c, collections.indexWhere(_.id == c)),\n        () => ())\n      momentDialog.show(fragmentManagerContext.manager, tagDialog)\n    }.toService()\n\n  def addCards(cards: Seq[Card]): TaskService[Unit] =\n    (Ui {\n      dom.getAdapter foreach (_.addCards(cards))\n    } ~\n      showList() ~\n      Ui(listener.firstItemInCollection())).toService()\n\n  def removeCards(cards: Seq[Card]): TaskService[Unit] =\n    (Ui {\n      dom.getAdapter foreach (_.removeCards(cards))\n    } ~ {\n      val emptyCollection = dom.getAdapter exists (_.collection.cards.isEmpty)\n      if (emptyCollection) {\n        listener.emptyCollection()\n        showEmptyMessage()\n      } else {\n        Ui.nop\n      }\n    }).toService()\n\n  def reloadCard(card: Card): TaskService[Unit] =\n    Ui {\n      dom.getAdapter foreach (_.updateCard(card))\n    }.toService()\n\n  def reloadCards(cards: Seq[Card]): TaskService[Unit] =\n    Ui {\n      dom.getAdapter foreach (_.updateCards(cards))\n    }.toService()\n\n  def showData(emptyCollection: Boolean): TaskService[Unit] =\n    (if (emptyCollection)\n       showEmptyMessage()\n     else\n       showList()).toService()\n\n  private[this] def showEmptyMessage(): Ui[Any] =\n    (dom.emptyCollectionMessage <~\n      tvText(messageText) <~\n      tvColor(statuses.theme.get(DrawerTextColor).alpha(0.8f))) ~\n      (dom.emptyCollectionView <~ vVisible <~ cvCardBackgroundColor(\n        statuses.theme.get(CardBackgroundColor))) ~\n      (dom.recyclerView <~ vGone)\n\n  private[this] def showList(): Ui[Any] =\n    (dom.recyclerView <~ vVisible) ~\n      (dom.emptyCollectionView <~ vGone)\n\n  private[this] def showMessage(message: Int): Ui[Any] = uiShortToast(message)\n\n  private[this] def openReorderMode: Ui[_] = {\n    listener.openReorderMode()\n    dom.pullToCloseView <~ pdvEnable(false)\n  }\n\n  private[this] def closeReorderMode(position: Int): Ui[_] = {\n    listener.closeReorderMode(position)\n    dom.pullToCloseView <~ pdvEnable(true)\n  }\n\n  private[this] def loadCollection(collection: Collection, animateCards: Boolean): Ui[_] = {\n\n    val adapterTweaks = if (!animateCards) {\n      rvAdapter(createAdapter(collection))\n    } else Tweak.blank\n\n    if (singleCollectionStatuses.activeFragment && collection.position == 0 && collection.cards.isEmpty)\n      listener.emptyCollection()\n\n    dom.recyclerView <~\n      rvLayoutManager(new GridLayoutManager(activityContextWrapper.application, numInLine)) <~\n      rvFixedSize <~\n      adapterTweaks <~\n      rvAddItemDecoration(new PaddingItemDecoration) <~\n      rvItemAnimator(new DefaultItemAnimator)\n  }\n\n  private[this] def createAdapter(collection: Collection) = {\n    val heightCard = {\n      val allPadding = (CardPadding.getPadding * 2) * 3\n      (dom.recyclerView.getHeight - allPadding - (dom.recyclerView.getPaddingBottom + dom.recyclerView.getPaddingTop)) / numInLine\n    }\n    CollectionAdapter(collection, heightCard, listener.performCard, listener.startReorderCards)\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/jobs/uiactions/ToolbarUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.jobs.uiactions\n\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.support.v7.app.AppCompatActivity\nimport cards.nine.app.ui.collections.CollectionsDetailsActivity._\nimport cards.nine.app.ui.collections.snails.CollectionsSnails._\nimport cards.nine.app.ui.commons.ops.CollectionOps._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.{ImplicitsUiExceptions, SystemBarsTint, UiContext}\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.commons.services.TaskService._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewTweaks._\n\nclass ToolbarUiActions(val dom: GroupCollectionsDOM, listener: GroupCollectionsUiListener)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_])\n    extends ImplicitsUiExceptions {\n\n  lazy val systemBarsTint = new SystemBarsTint\n\n  val resistanceDisplacement = .2f\n\n  val resistanceScale = .05f\n\n  lazy val spaceMove = resGetDimensionPixelSize(R.dimen.space_moving_collection_details)\n\n  lazy val maxHeightToolbar = resGetDimensionPixelSize(R.dimen.height_toolbar_collection_details)\n\n  def initialize(\n      backgroundColor: Int,\n      initialColor: Int,\n      iconCollection: String,\n      isStateChanged: Boolean): TaskService[Unit] =\n    (Ui {\n      activityContextWrapper.original.get match {\n        case Some(activity: AppCompatActivity) =>\n          val iconIndicatorDrawable = PathMorphDrawable(\n            defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_default),\n            padding = resGetDimensionPixelSize(R.dimen.padding_icon_home_indicator))\n          statuses = statuses.copy(iconHome = Option(iconIndicatorDrawable))\n          activity.setSupportActionBar(dom.toolbar)\n          activity.getSupportActionBar.setDisplayHomeAsUpEnabled(true)\n          activity.getSupportActionBar.setHomeAsUpIndicator(iconIndicatorDrawable)\n        case _ =>\n      }\n    } ~\n      (dom.root <~ vBackgroundColor(backgroundColor)) ~\n      systemBarsTint.initSystemStatusBarTint() ~\n      updateToolbarColor(initialColor) ~\n      (dom.icon <~ ivSrc(iconCollection.getIconDetail)) ~\n      (if (isStateChanged) Ui.nop else dom.toolbar <~ enterToolbar)).toService()\n\n  def pullCloseScrollY(scroll: Int, close: Boolean): TaskService[Unit] = {\n    val displacement         = scroll * resistanceDisplacement\n    val distanceToValidClose = resGetDimension(R.dimen.distance_to_valid_action)\n    val scale                = 1f + ((scroll / distanceToValidClose) * resistanceScale)\n    ((dom.iconContent <~ vScaleX(scale) <~ vScaleY(scale) <~ vTranslationY(displacement)) ~\n      Ui {\n        val newIcon = if (close) IconTypes.CLOSE else IconTypes.BACK\n        statuses.iconHome match {\n          case Some(icon) if icon.currentTypeIcon != newIcon && !icon.isRunning =>\n            icon.setToTypeIcon(newIcon)\n            icon.start()\n          case _ =>\n        }\n      }).toService()\n  }\n\n  private[this] def updateToolbarColor(color: Int): Ui[Any] =\n    (dom.toolbar <~ vBackgroundColor(color)) ~\n      systemBarsTint.updateStatusColor(color)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/snails/CollectionsSnails.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.snails\n\nimport android.animation.{Animator, AnimatorListenerAdapter}\nimport android.graphics.Point\nimport android.view.View\nimport android.view.animation.{AccelerateDecelerateInterpolator, DecelerateInterpolator}\nimport macroid.extras.UIActionsExtras._\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.preferences.commons.SpeedAnimations\nimport cards.nine.commons._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\n\nimport scala.concurrent.Promise\n\nobject CollectionsSnails {\n\n  def animationEnterTitle(implicit context: ContextWrapper) = {\n    val distance = resGetDimensionPixelSize(R.dimen.padding_large)\n    val duration = resGetInteger(R.integer.anim_duration_icon_collection_detail)\n    vTranslationY(distance) +\n      vVisible +\n      vAlpha(0) ++\n      applyAnimation(duration = Option(duration), y = Option(0), alpha = Option(1))\n  }\n\n  def animationOutTitle(implicit context: ContextWrapper) = {\n    val distance = resGetDimensionPixelSize(R.dimen.padding_large)\n    val duration = resGetInteger(R.integer.anim_duration_icon_collection_detail)\n    applyAnimation(duration = Option(duration), y = Option(distance), alpha = Option(1)) +\n      vGone\n  }\n\n  def animationIcon(fromLeft: Boolean, resIcon: Int)(implicit context: ContextWrapper) = {\n    val distance =\n      if (fromLeft) -resGetDimensionPixelSize(R.dimen.padding_default)\n      else resGetDimensionPixelSize(R.dimen.padding_default)\n    val duration = resGetInteger(R.integer.anim_duration_icon_collection_detail)\n    applyAnimation(\n      duration = Option(duration),\n      x = Option(-distance),\n      alpha = Option(0),\n      scaleX = Option(0.7f),\n      scaleY = Option(0.7f)) +\n      vTranslationX(distance) +\n      ivSrc(resIcon) ++\n      applyAnimation(\n        duration = Option(duration),\n        x = Option(0),\n        alpha = Option(1),\n        scaleX = Option(1f),\n        scaleY = Option(1f)) +\n      vRotation(0)\n  }\n\n  def enterToolbar(implicit activityContextWrapper: ActivityContextWrapper): Snail[View] = {\n    val display =\n      activityContextWrapper.getOriginal.getWindowManager.getDefaultDisplay\n    val size = new Point()\n    display.getSize(size)\n    val height = size.y\n    val times  = height.toFloat / resGetDimension(R.dimen.height_toolbar_collection_details)\n    vScaleY(times) ++ applyAnimation(scaleY = Some(1))\n  }\n\n  def enterViews(implicit context: ContextWrapper): Snail[View] = Snail[View] { view =>\n    view.clearAnimation()\n    view.setLayerType(View.LAYER_TYPE_HARDWARE, javaNull)\n    val animPromise = Promise[Unit]()\n    view.setAlpha(0)\n    view.animate\n      .setDuration(SpeedAnimations.getDuration)\n      .setInterpolator(new DecelerateInterpolator())\n      .alpha(1f)\n      .setListener(new AnimatorListenerAdapter {\n        override def onAnimationEnd(animation: Animator): Unit = {\n          super.onAnimationEnd(animation)\n          view.setLayerType(View.LAYER_TYPE_NONE, javaNull)\n          animPromise.trySuccess(())\n        }\n      })\n      .start()\n    animPromise.future\n  }\n\n  def exitViews(implicit context: ContextWrapper): Snail[View] = Snail[View] { view =>\n    view.clearAnimation()\n    view.setLayerType(View.LAYER_TYPE_HARDWARE, javaNull)\n    val animPromise = Promise[Unit]()\n    val move =\n      resGetDimensionPixelSize(R.dimen.space_enter_views_collection_detail)\n    view.animate\n      .setDuration(SpeedAnimations.getDuration)\n      .setInterpolator(new AccelerateDecelerateInterpolator())\n      .translationY(move)\n      .alpha(0)\n      .setListener(new AnimatorListenerAdapter {\n        override def onAnimationEnd(animation: Animator): Unit = {\n          super.onAnimationEnd(animation)\n          view.setLayerType(View.LAYER_TYPE_NONE, javaNull)\n          animPromise.trySuccess(())\n        }\n      })\n      .start()\n    animPromise.future\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/collections/tasks/CollectionJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.collections.tasks\n\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ActivityContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.models.Application.ApplicationDataOps\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.{GetByName, PublishedByOther}\nimport cards.nine.models.{ApplicationData, Collection, SharedCollection, SharedCollectionPackage}\n\ntrait CollectionJobs { self: Jobs with Conversions =>\n\n  def addSharedCollection(sharedCollection: SharedCollection)(\n      implicit contextSupport: ActivityContextSupport): TaskService[Collection] = {\n\n    def getCards(appsInstalled: Seq[ApplicationData], packages: Seq[SharedCollectionPackage]) =\n      packages map { pck =>\n        appsInstalled find (_.packageName == pck.packageName) map (_.toCardData) getOrElse toCardData(\n          pck)\n      }\n\n    for {\n      appsInstalled <- di.deviceProcess.getSavedApps(GetByName)\n      collection <- di.collectionProcess.addCollection(\n        toCollectionDataFromSharedCollection(\n          sharedCollection,\n          getCards(appsInstalled, sharedCollection.resolvedPackages)))\n      _ <- sharedCollection.publicCollectionStatus match {\n        case PublishedByOther =>\n          di.sharedCollectionsProcess\n            .subscribe(sharedCollection.sharedCollectionId)\n            .resolveLeftTo(Right((): Unit))\n        case _ => TaskService.empty\n      }\n    } yield collection\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ActivityFindViews.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons\n\nimport android.app.Activity\nimport android.view.ViewGroup\nimport cats.data.{Reader, _}\nimport com.fortysevendeg.ninecardslauncher.TypedResource\n\nobject ActivityFindViews {\n  def findView[A](tr: TypedResource[A]): Reader[Activity, A] =\n    Reader((activity: Activity) => activity.findViewById(tr.id).asInstanceOf[A])\n}\n\nobject ViewGroupFindViews {\n  def findView[A](tr: TypedResource[A]): Reader[ViewGroup, A] =\n    Reader((viewGroup: ViewGroup) => viewGroup.findViewById(tr.id).asInstanceOf[A])\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/AppLog.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons\n\nimport android.util.Log\nimport com.crashlytics.android.Crashlytics\n\nobject AppLog {\n\n  val tag = \"9cards\"\n\n  def invalidConfigurationV2: Unit = info(\"Invalid configuration for backend V2\")\n\n  def info(message: String): Unit = Log.i(tag, message)\n\n  def printErrorMessage(ex: Throwable, message: Option[String] = None): Unit = {\n    try {\n      val outputEx = Option(ex.getCause) getOrElse ex\n      Log.e(tag, message getOrElse errorMessage(outputEx), outputEx)\n      Crashlytics.logException(ex)\n    } catch { case _: Throwable => }\n  }\n\n  def printErrorTaskMessage(header: String, ex: Throwable): Unit = {\n    try {\n      Log.e(tag, header)\n      printErrorMessage(ex, Some(errorMessage(ex)))\n    } catch { case _: Throwable => }\n  }\n\n  private[this] def errorMessage(ex: Throwable): String =\n    Option(ex.getMessage) getOrElse ex.getClass.toString\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/AsyncImageTweaks.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons\n\nimport java.io.{File, InputStream}\n\nimport android.content.{ContentResolver, UriMatcher}\nimport android.graphics.Bitmap\nimport android.graphics.drawable.Drawable\nimport android.net.Uri\nimport android.provider.ContactsContract\nimport android.provider.ContactsContract.Contacts\nimport android.widget.ImageView\nimport android.widget.ImageView.ScaleType\nimport com.bumptech.glide.load.data.DataFetcher\nimport com.bumptech.glide.load.model.stream.StreamModelLoader\nimport com.bumptech.glide.load.resource.drawable.GlideDrawable\nimport com.bumptech.glide.request.animation.GlideAnimation\nimport com.bumptech.glide.request.target.ViewTarget\nimport com.bumptech.glide._\nimport com.bumptech.glide.load.resource.bitmap.{BitmapEncoder, StreamBitmapDecoder}\nimport com.bumptech.glide.load.resource.file.FileToStreamDecoder\nimport com.bumptech.glide.signature.StringSignature\nimport macroid.extras.ImageViewTweaks._\nimport cards.nine.app.ui.commons.glide.{\n  AppIconLoader,\n  ApplicationIconDecoder,\n  IconFromPackageDecoder,\n  IconFromPackageLoader\n}\nimport cards.nine.app.ui.components.drawables.CharDrawable\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\n\nimport scala.util.{Failure, Success, Try}\n\nobject AsyncImageTweaks {\n  type W = ImageView\n\n  def ivUri(uri: String)(implicit context: UiContext[_]): Tweak[W] = Tweak[W] { imageView =>\n    glide() foreach { glide =>\n      glide.load(uri).crossFade().into(imageView)\n    }\n  }\n\n  def ivSrcByPackageName(maybePackageName: Option[String], term: String)(\n      implicit context: UiContext[_],\n      contextWrapper: ContextWrapper): Tweak[W] = Tweak[W](\n    imageView => {\n      (glide(), maybePackageName) match {\n        case (Some(glide), Some(packageName)) =>\n          glide\n            .using(new AppIconLoader, classOf[String])\n            .from(classOf[String])\n            .as(classOf[Bitmap])\n            .decoder(new ApplicationIconDecoder(packageName))\n            .cacheDecoder(new FileToStreamDecoder(\n              new StreamBitmapDecoder(contextWrapper.application)))\n            .encoder(new BitmapEncoder())\n            .load(packageName)\n            .signature(getSignature(packageName))\n            .into(imageView)\n        case _ =>\n          (imageView <~ ivSrc(CharDrawable(term.charAt(0).toString, circle = true))).run\n      }\n    }\n  )\n\n  private[this] def getSignature(packageName: String)(\n      implicit contextWrapper: ContextWrapper): StringSignature =\n    Try {\n      contextWrapper.application.getPackageManager.getPackageInfo(packageName, 0).lastUpdateTime\n    } match {\n      case Success(time) => new StringSignature(s\"$packageName$time\")\n      case Failure(_)    => new StringSignature(packageName)\n    }\n\n  def ivSrcIconFromPackage(packageName: String, icon: Int, term: String)(\n      implicit context: UiContext[_],\n      contextWrapper: ContextWrapper): Tweak[W] = Tweak[W](\n    imageView => {\n      glide() match {\n        case Some(glide) =>\n          glide\n            .using(new IconFromPackageLoader, classOf[Int])\n            .from(classOf[Int])\n            .as(classOf[Bitmap])\n            .decoder(new IconFromPackageDecoder(packageName))\n            .cacheDecoder(new FileToStreamDecoder(\n              new StreamBitmapDecoder(contextWrapper.application)))\n            .encoder(new BitmapEncoder())\n            .load(icon)\n            .into(imageView)\n        case _ =>\n          (imageView <~ ivSrc(CharDrawable(term.charAt(0).toString, circle = true))).run\n      }\n    }\n  )\n\n  def ivCardUri(maybeUri: Option[String], name: String, circular: Boolean = false)(\n      implicit context: ContextWrapper,\n      uiContext: UiContext[_]): Tweak[W] =\n    Tweak[W](imageView => {\n      glide() foreach { glide =>\n        val request = maybeUri match {\n          case Some(uri) if new File(uri).exists() =>\n            Try(glide.load(uri)).toOption\n          case _ => None\n        }\n        loadCardRequest(\n          imageView = imageView,\n          maybeRequest = request,\n          char = name.substring(0, 1),\n          circular = circular)\n      }\n    })\n\n  def ivUriContactFromLookup(maybeLookup: Option[String], name: String, circular: Boolean = false)(\n      implicit context: ContextWrapper,\n      uiContext: UiContext[_]): Tweak[W] =\n    Tweak[W](imageView => {\n      glide() foreach { glide =>\n        val uri = Uri.parse(ContactPhotoLoader.getUrl(maybeLookup))\n        makeRequest(\n          request =\n            glide.using(new ContactPhotoLoader(context.application.getContentResolver)).load(uri),\n          imageView = imageView,\n          char = name.substring(0, 1),\n          circular = circular,\n          fadeInFailed = false)\n      }\n    })\n\n  def ivUriContact(uri: String, name: String, circular: Boolean = false)(\n      implicit context: ContextWrapper,\n      uiContext: UiContext[_]): Tweak[W] =\n    Tweak[W](imageView => {\n      glide() foreach { glide =>\n        makeRequest(\n          request = glide\n            .using(new ContactPhotoLoader(context.application.getContentResolver))\n            .load(Uri.parse(uri)),\n          imageView = imageView,\n          char = name.substring(0, 1),\n          circular = circular,\n          fadeInFailed = false)\n      }\n    })\n\n  def ivUriContactInfo(uri: String, header: Boolean = true)(\n      implicit context: ContextWrapper,\n      uiContext: UiContext[_]): Tweak[W] =\n    Tweak[W](imageView => {\n      glide() foreach { glide =>\n        makeContactRequest(\n          request = glide\n            .using(new ContactPhotoLoader(context.application.getContentResolver))\n            .load(Uri.parse(uri)),\n          imageView = imageView,\n          header = header,\n          fadeInFailed = false)\n      }\n    })\n\n  private[this] def glide()(implicit uiContext: UiContext[_]): Option[RequestManager] =\n    Try {\n      uiContext match {\n        case c: ApplicationUiContext => Glide.`with`(c.value)\n        case c: ActivityUiContext    => Glide.`with`(c.value)\n        case c: FragmentUiContext    => Glide.`with`(c.value)\n        case c: GenericUiContext     => Glide.`with`(c.value)\n      }\n    }.toOption\n\n  private[this] def loadCardRequest(\n      imageView: ImageView,\n      maybeRequest: Option[DrawableTypeRequest[_]],\n      char: String,\n      circular: Boolean = false)(implicit context: ContextWrapper, uiContext: UiContext[_]) = {\n    maybeRequest match {\n      case Some(request) =>\n        makeRequest(request = request, imageView = imageView, char = char, circular = circular)\n      case _ => (imageView <~ ivSrc(CharDrawable(char, circle = circular))).run\n    }\n  }\n\n  private[this] def makeRequest(\n      request: DrawableTypeRequest[_],\n      imageView: ImageView,\n      char: String,\n      circular: Boolean = false,\n      fadeInFailed: Boolean = true)(implicit context: ContextWrapper) = {\n    request\n      .crossFade()\n      .into(new ViewTarget[ImageView, GlideDrawable](imageView) {\n        override def onLoadStarted(placeholder: Drawable): Unit =\n          view.setImageDrawable(javaNull)\n        override def onLoadFailed(e: Exception, errorDrawable: Drawable): Unit =\n          (view <~ ivSrc(CharDrawable(char, circle = circular)) <~ (if (fadeInFailed)\n                                                                      fadeIn(200)\n                                                                    else\n                                                                      Snail.blank)).run\n        override def onResourceReady(\n            resource: GlideDrawable,\n            glideAnimation: GlideAnimation[_ >: GlideDrawable]): Unit =\n          view.setImageDrawable(resource.getCurrent)\n      })\n  }\n\n  private[this] def makeContactRequest(\n      request: DrawableTypeRequest[_],\n      imageView: ImageView,\n      header: Boolean,\n      fadeInFailed: Boolean = true)(implicit context: ContextWrapper) = {\n    request\n      .crossFade()\n      .into(new ViewTarget[ImageView, GlideDrawable](imageView) {\n        override def onLoadStarted(placeholder: Drawable): Unit =\n          imageView.setImageDrawable(javaNull)\n        override def onLoadFailed(e: Exception, errorDrawable: Drawable): Unit =\n          if (header) loadDefaultHeaderImage(imageView, fadeInFailed)\n          else loadDefaultGeneralInfoImage(imageView, fadeInFailed)\n        override def onResourceReady(\n            resource: GlideDrawable,\n            glideAnimation: GlideAnimation[_ >: GlideDrawable]): Unit =\n          view.setImageDrawable(resource.getCurrent)\n      })\n  }\n\n  private[this] def loadDefaultHeaderImage(imageView: ImageView, fadeInFailed: Boolean = true) =\n    (imageView <~ ivSrc(R.drawable.dialog_contact_header_no_image) <~ ivScaleType(\n      ScaleType.CENTER_INSIDE) <~ (if (fadeInFailed) fadeIn(200)\n                                   else Snail.blank)).run\n\n  private[this] def loadDefaultGeneralInfoImage(\n      imageView: ImageView,\n      fadeInFailed: Boolean = true) =\n    (imageView <~ ivSrc(R.drawable.dialog_contact_icon_general_info) <~ (if (fadeInFailed)\n                                                                           fadeIn(200)\n                                                                         else\n                                                                           Snail.blank)).run\n\n  class ContactPhotoLoader(contentResolver: ContentResolver) extends StreamModelLoader[Uri] {\n\n    // A lookup uri (e.g. content://com.android.contacts/contacts/lookup/3570i61d948d30808e537)\n    val idLookup = 1\n    // A contact thumbnail uri (e.g. content://com.android.contacts/contacts/38/photo)\n    val idThumbnail = 2\n    // A contact uri (e.g. content://com.android.contacts/contacts/38)\n    val idContact = 3\n    // A contact display photo (high resolution) uri (e.g. content://com.android.contacts/display_photo/5)\n    val idDisplayPhoto = 4\n\n    val matcher = new UriMatcher(UriMatcher.NO_MATCH)\n    matcher.addURI(ContactsContract.AUTHORITY, \"contacts/lookup/*/#\", idLookup)\n    matcher.addURI(ContactsContract.AUTHORITY, \"contacts/lookup/*\", idLookup)\n    matcher.addURI(ContactsContract.AUTHORITY, \"contacts/#/photo\", idThumbnail)\n    matcher.addURI(ContactsContract.AUTHORITY, \"contacts/#\", idContact)\n    matcher.addURI(ContactsContract.AUTHORITY, \"display_photo/#\", idDisplayPhoto)\n\n    override def getResourceFetcher(\n        model: Uri,\n        width: Int,\n        height: Int): DataFetcher[InputStream] =\n      new DataFetcher[InputStream]() {\n\n        override def loadData(priority: Priority): InputStream =\n          matcher.`match`(model) match {\n            case `idLookup` =>\n              Option(ContactsContract.Contacts.lookupContact(contentResolver, model)) match {\n                case Some(u) =>\n                  Contacts.openContactPhotoInputStream(contentResolver, u, true)\n                case _ => javaNull\n              }\n            case `idContact` =>\n              Contacts.openContactPhotoInputStream(contentResolver, model, true)\n            case `idThumbnail`    => contentResolver.openInputStream(model)\n            case `idDisplayPhoto` => contentResolver.openInputStream(model)\n            case _                => javaNull\n          }\n\n        override def getId: String = model.toString\n\n        override def cleanup(): Unit = {}\n\n        override def cancel(): Unit = {}\n      }\n  }\n\n  object ContactPhotoLoader {\n    def getUrl(maybeLookup: Option[String]) =\n      s\"content://${ContactsContract.AUTHORITY}/contacts/lookup/${maybeLookup.getOrElse(\"\")}\"\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/Commons.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons\n\nimport android.graphics.Color\nimport cards.nine.models._\nimport cards.nine.models.types.theme._\nimport com.fortysevendeg.ninecardslauncher.R\n\nobject Constants {\n\n  val numSpaces = 9\n\n  val numInLine = 3\n\n}\n\nobject RequestCodes {\n\n  val shortcutAdded = 1\n\n  val selectInfoContact = 2\n\n  val selectInfoIcon = 3\n\n  val selectInfoColor = 4\n\n  val resolveGooglePlayConnection = 5\n\n  val resolveConnectedUser = 6\n\n  val goToProfile = 7\n\n  val goToCollectionDetails = 8\n\n  val goToPreferences = 9\n\n  val goToWidgets = 10\n\n  val goToConfigureWidgets = 11\n\n  val contactsPermission = 13\n\n  val callLogPermission = 14\n\n  val phoneCallPermission = 15\n\n  val locationPermission = 16\n\n  val wizardPermissions = 17\n\n  val selectAccount = 18\n\n}\n\nobject ResultCodes {\n\n  val logoutSuccessful = 10\n\n  val preferencesChanged = 20\n\n}\n\nobject ResultData {\n\n  val preferencesResultData = \"preferences-result-data\"\n\n}\n\nobject SyncDeviceState {\n  val stateSyncing = \"sync-device-state-syncing\"\n  val stateSuccess = \"sync-device-state-success\"\n  val stateFailure = \"sync-device-state-failure\"\n}\n\nobject AppUtils {\n\n  def getDefaultTheme =\n    NineCardsTheme(\n      name = \"light\",\n      parent = ThemeLight,\n      styles = Seq(\n        ThemeStyle(PrimaryColor, Color.parseColor(\"#3F51B5\")),\n        ThemeStyle(SearchBackgroundColor, Color.parseColor(\"#ffffff\")),\n        ThemeStyle(SearchPressedColor, Color.parseColor(\"#ff59afdd\")),\n        ThemeStyle(SearchGoogleColor, Color.parseColor(\"#a3a3a3\")),\n        ThemeStyle(SearchIconsColor, Color.parseColor(\"#646464\")),\n        ThemeStyle(SearchTextColor, Color.parseColor(\"#646464\")),\n        ThemeStyle(DrawerTabsBackgroundColor, Color.parseColor(\"#16000000\")),\n        ThemeStyle(DrawerBackgroundColor, Color.parseColor(\"#ffffff\")),\n        ThemeStyle(DrawerTextColor, Color.parseColor(\"#cc000000\")),\n        ThemeStyle(DrawerIconColor, Color.parseColor(\"#99000000\")),\n        ThemeStyle(DockPressedColor, Color.parseColor(\"#ffd5f2fa\")),\n        ThemeStyle(CardLayoutBackgroundColor, Color.parseColor(\"#eeeeee\")),\n        ThemeStyle(CardTextColor, Color.parseColor(\"#000000\")),\n        ThemeStyle(CardBackgroundColor, Color.parseColor(\"#ffffff\")),\n        ThemeStyle(CardBackgroundPressedColor, Color.parseColor(\"#000000\")),\n        ThemeStyle(CollectionDetailTextTabSelectedColor, Color.parseColor(\"#ffffff\")),\n        ThemeStyle(CollectionDetailTextTabDefaultColor, Color.parseColor(\"#80ffffff\"))),\n      themeColors = ThemeColors(Color.parseColor(\"#FF9800\"), Seq.empty))\n}\n\nobject Team {\n\n  val team = Seq(\n    (\"Ana\", R.drawable.ana),\n    (\"Domin\", R.drawable.domin),\n    (\"Fede\", R.drawable.fede),\n    (\"Javi\", R.drawable.javi_pacheco),\n    (\"Jorge\", R.drawable.jorge_galindo),\n    (\"Paco\", R.drawable.paco),\n    (\"Raúl\", R.drawable.raul_raja),\n    (\"Diego\", R.drawable.diego),\n    (\"Isra\", R.drawable.isra),\n    (\"Aaron\", R.drawable.aaron),\n    (\"Ale\", R.drawable.ale),\n    (\"Andy\", R.drawable.andy),\n    (\"Javi\", R.drawable.javi_siloniz),\n    (\"Benjy\", R.drawable.benjy),\n    (\"Fran\", R.drawable.fran),\n    (\"John\", R.drawable.john),\n    (\"Juan\", R.drawable.juan),\n    (\"Juan Pedro\", R.drawable.juan_pedro),\n    (\"Justin\", R.drawable.justin),\n    (\"Nick\", R.drawable.nick),\n    (\"Maureen\", R.drawable.maureen),\n    (\"Noel\", R.drawable.noel),\n    (\"Rafa\", R.drawable.rafa))\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/CommonsExcerpt.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons\n\nimport android.support.v4.view.GravityCompat\nimport android.support.v4.widget.DrawerLayout\nimport android.view.View\nimport android.widget.TextView\nimport macroid.Excerpt\n\nobject CommonsExcerpt {\n\n  def height = Excerpt[View, Int](_.getHeight)\n\n  def width = Excerpt[View, Int](_.getWidth)\n\n  def isVisible = Excerpt[View, Boolean](_.getVisibility == View.VISIBLE)\n\n  def isGone = Excerpt[View, Boolean](_.getVisibility == View.GONE)\n\n  def isInvisible = Excerpt[View, Boolean](_.getVisibility == View.INVISIBLE)\n\n  def isEnabled = Excerpt[View, Boolean](_.isEnabled)\n\n  def text =\n    Excerpt[TextView, Option[String]](tv => Option(tv.getText) map (_.toString))\n\n  def dlIsLockedClosedDrawerStart: Excerpt[DrawerLayout, Boolean] =\n    Excerpt[DrawerLayout, Boolean](\n      _.getDrawerLockMode(GravityCompat.START) == DrawerLayout.LOCK_MODE_LOCKED_CLOSED)\n\n  def dlIsLockedClosedDrawerEnd: Excerpt[DrawerLayout, Boolean] =\n    Excerpt[DrawerLayout, Boolean](\n      _.getDrawerLockMode(GravityCompat.END) == DrawerLayout.LOCK_MODE_LOCKED_CLOSED)\n\n  def dlIsLockedOpenedDrawerStart: Excerpt[DrawerLayout, Boolean] =\n    Excerpt[DrawerLayout, Boolean](\n      _.getDrawerLockMode(GravityCompat.START) == DrawerLayout.LOCK_MODE_LOCKED_OPEN)\n\n  def dlIsLockedOpenedDrawerEnd: Excerpt[DrawerLayout, Boolean] =\n    Excerpt[DrawerLayout, Boolean](\n      _.getDrawerLockMode(GravityCompat.END) == DrawerLayout.LOCK_MODE_LOCKED_OPEN)\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/CommonsTweak.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons\n\nimport java.io.Closeable\n\nimport android.annotation.SuppressLint\nimport android.content.ClipData\nimport android.content.res.ColorStateList\nimport android.graphics.Paint\nimport android.graphics.drawable._\nimport android.graphics.drawable.shapes.OvalShape\nimport android.os.Bundle\nimport android.support.design.widget.Snackbar\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.support.v7.widget.{ListPopupWindow, RecyclerView}\nimport android.view.View.{DragShadowBuilder, OnClickListener}\nimport android.view.{Gravity, View, ViewGroup}\nimport android.widget.AdapterView.OnItemClickListener\nimport android.widget._\nimport cards.nine.app.ui.commons.AppLog._\nimport cards.nine.app.ui.commons.dialogs.wizard._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.adapters.ThemeArrayAdapter\nimport cards.nine.app.ui.components.drawables.DrawerBackgroundDrawable\nimport cards.nine.app.ui.launcher.snails.LauncherSnails._\nimport cards.nine.app.ui.launcher.types.{DragLauncherType, DragObject}\nimport cards.nine.commons._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.{DrawerBackgroundColor, DrawerTextColor, PrimaryColor}\nimport com.fortysevendeg.ninecardslauncher.R\nimport com.google.android.flexbox.FlexboxLayout\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.DeviceVersion.{KitKat, Lollipop}\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\n\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.util.{Failure, Random, Try}\n\nobject CommonsTweak {\n\n  val appsByRow = 5\n\n  def vBackgroundBoxWorkspace(color: Int, horizontalPadding: Int = 0, verticalPadding: Int = 0)(\n      implicit contextWrapper: ContextWrapper): Tweak[View] = {\n    val radius = resGetDimensionPixelSize(R.dimen.radius_default)\n    Lollipop.ifSupportedThen {\n      vBackgroundColor(color) +\n        vClipBackground(\n          radius,\n          horizontalPadding = horizontalPadding,\n          verticalPadding = verticalPadding) +\n        vElevation(resGetDimensionPixelSize(R.dimen.elevation_box_workspaces))\n    } getOrElse {\n      val drawable =\n        new DrawerBackgroundDrawable(color, horizontalPadding, verticalPadding, radius)\n      vBackground(drawable)\n    }\n  }\n\n  def vBackgroundCollection(indexColor: Int)(\n      implicit contextWrapper: ContextWrapper,\n      theme: NineCardsTheme): Tweak[View] =\n    vBackgroundCircle(theme.getIndexColor(indexColor))\n\n  @SuppressLint(Array(\"NewApi\"))\n  def vBackgroundCircle(color: Int)(implicit contextWrapper: ContextWrapper): Tweak[View] = {\n    def createShapeDrawable(c: Int) = {\n      val drawableColor = new ShapeDrawable(new OvalShape())\n      drawableColor.getPaint.setColor(c)\n      drawableColor.getPaint.setStyle(Paint.Style.FILL)\n      drawableColor.getPaint.setAntiAlias(true)\n      drawableColor\n    }\n\n    def getDrawable(c: Int): Drawable = {\n      val drawableColor  = createShapeDrawable(c)\n      val padding        = resGetDimensionPixelSize(R.dimen.elevation_default)\n      val drawableShadow = createShapeDrawable(resGetColor(R.color.shadow_default))\n      val layer          = new LayerDrawable(Array(drawableShadow, drawableColor))\n      layer.setLayerInset(0, padding, padding, padding, 0)\n      layer.setLayerInset(1, padding, 0, padding, padding)\n      layer\n    }\n    val elevation = resGetDimensionPixelSize(R.dimen.elevation_default)\n\n    vBackground(Lollipop ifSupportedThen {\n      new RippleDrawable(\n        new ColorStateList(Array(Array()), Array(color.dark(0.2f))),\n        createShapeDrawable(color),\n        javaNull)\n    } getOrElse {\n      val states = new StateListDrawable()\n      states.addState(Array[Int](android.R.attr.state_pressed), getDrawable(color.dark()))\n      states.addState(Array.emptyIntArray, getDrawable(color))\n      states\n    }) + (Lollipop ifSupportedThen vElevation(elevation) getOrElse Tweak.blank)\n  }\n\n  def vSetPosition(position: Int): Tweak[View] = vTag(R.id.position, position)\n\n  def vSetType(t: String) = vTag(R.id.view_type, t)\n\n  def vAddField[T](key: String, value: T) = Tweak[View] { view =>\n    view.setTag(R.id.fields_map, view.getFieldsMap + ((key, value)))\n  }\n\n  def vRemoveField(key: String) = Tweak[View] { view =>\n    view.setTag(R.id.fields_map, view.getFieldsMap - key)\n  }\n\n  def vUseLayerHardware = vTag(R.id.use_layer_hardware, \"\")\n\n  def vLayerHardware(activate: Boolean) = Transformer {\n    case v: View if v.hasLayerHardware =>\n      v <~ (if (activate) vLayerTypeHardware() else vLayerTypeNone())\n  }\n\n  def vListThemedPopupWindowShow(\n      icons: Seq[Int] = Seq.empty,\n      values: Seq[String],\n      onItemClickListener: (Int) ⇒ Unit,\n      width: Option[Int] = None,\n      height: Option[Int] = None,\n      horizontalOffset: Option[Int] = None\n  )(implicit contextWrapper: ContextWrapper, theme: NineCardsTheme) =\n    Tweak[View] { view =>\n      val listPopupWindow = new ListPopupWindow(contextWrapper.bestAvailable)\n      listPopupWindow.setAdapter(new ThemeArrayAdapter(icons, values))\n      listPopupWindow.setAnchorView(view)\n      horizontalOffset foreach listPopupWindow.setHorizontalOffset\n      width foreach listPopupWindow.setWidth\n      height foreach listPopupWindow.setHeight\n      listPopupWindow.setModal(true)\n      listPopupWindow.setOnItemClickListener(new OnItemClickListener {\n        override def onItemClick(\n            parent: AdapterView[_],\n            view: View,\n            position: Int,\n            id: Long): Unit = {\n          onItemClickListener(position)\n          listPopupWindow.dismiss()\n        }\n      })\n      listPopupWindow.show()\n    }\n\n  def ivReloadPager(currentPage: Int)(implicit contextWrapper: ContextWrapper) = Transformer {\n    case imageView: ImageView if imageView.isPosition(currentPage) =>\n      imageView <~ vActivated(true) <~~ pagerAppear\n    case imageView: ImageView =>\n      imageView <~ vActivated(false)\n  }\n\n  def vLauncherWizardSnackbar(\n      wizardInlineType: WizardInlineType,\n      forceNavigationBarHeight: Boolean = true)(\n      implicit contextWrapper: ContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      theme: NineCardsTheme,\n      systemBarsTint: SystemBarsTint): Tweak[View] = Tweak[View] { view =>\n    def showDialog = Ui {\n      val dialog = new WizardInlineFragment()\n      val bundle = new Bundle()\n      bundle.putString(WizardInlineFragment.wizardInlineTypeKey, wizardInlineType.toString)\n      dialog.setArguments(bundle)\n      dialog.show(fragmentManagerContext.manager, \"wizard-inline-dialog\")\n    }\n\n    import cards.nine.app.ui.commons.Team._\n\n    val (userSelectedName, userSelectedIcon) =\n      team(Random.nextInt(team.length))\n    val text = wizardInlineType match {\n      case AppDrawerWizardInline =>\n        resGetString(R.string.wizard_inline_message_app_drawer, userSelectedName)\n      case CollectionsWizardInline =>\n        resGetString(R.string.wizard_inline_message_collections, userSelectedName)\n      case LauncherWizardInline =>\n        resGetString(R.string.wizard_inline_message_launcher, userSelectedName)\n      case ProfileWizardInline =>\n        resGetString(R.string.wizard_inline_message_profile, userSelectedName)\n    }\n    val snackbar = Snackbar.make(view, text, Snackbar.LENGTH_INDEFINITE)\n    val rootView = snackbar.getView.asInstanceOf[ViewGroup]\n    (rootView <~\n      vBackgroundColor(theme.get(DrawerBackgroundColor)) <~\n      Transformer {\n        case button: Button =>\n          button <~\n            tvColor(theme.get(PrimaryColor))\n        case textView: TextView =>\n          textView <~\n            tvColor(theme.get(DrawerTextColor)) <~\n            tvMaxLines(3) <~\n            tvDrawablePadding(resGetDimensionPixelSize(R.dimen.padding_default)) <~\n            tvGravity(Gravity.CENTER_VERTICAL) <~\n            tvCompoundDrawablesWithIntrinsicBoundsResources(left = userSelectedIcon) <~\n            On.click(showDialog ~ Ui(snackbar.dismiss()))\n      }).run\n    (forceNavigationBarHeight, rootView.getLayoutParams) match {\n      case (true, params: FrameLayout.LayoutParams) =>\n        val bottom = KitKat.ifSupportedThen(systemBarsTint.getNavigationBarHeight) getOrElse 0\n        params.setMargins(0, 0, 0, bottom)\n        snackbar.getView.setLayoutParams(params)\n      case _ =>\n    }\n    snackbar.setAction(R.string.wizard_inline_show, new OnClickListener {\n      override def onClick(v: View): Unit = showDialog.run\n    })\n    snackbar.show()\n  }\n\n  def vLauncherSnackbar(\n      message: Int,\n      args: Seq[String] = Seq.empty,\n      length: Int = Snackbar.LENGTH_SHORT)(\n      implicit contextWrapper: ContextWrapper,\n      systemBarsTint: SystemBarsTint): Tweak[View] = Tweak[View] { view =>\n    val snackbar =\n      Snackbar.make(view, contextWrapper.application.getString(message, args: _*), length)\n    snackbar.getView.getLayoutParams match {\n      case params: FrameLayout.LayoutParams =>\n        val bottom = KitKat.ifSupportedThen(systemBarsTint.getNavigationBarHeight) getOrElse 0\n        params.setMargins(0, 0, 0, bottom)\n        snackbar.getView.setLayoutParams(params)\n      case _ =>\n    }\n    snackbar.show()\n  }\n\n  def vLauncherSnackbarWithAction(\n      message: Int,\n      resAction: Int,\n      action: () => Unit,\n      args: Seq[String] = Seq.empty,\n      length: Int = Snackbar.LENGTH_SHORT)(\n      implicit contextWrapper: ContextWrapper,\n      systemBarsTint: SystemBarsTint): Tweak[View] = Tweak[View] { view =>\n    val snackbar =\n      Snackbar.make(view, contextWrapper.application.getString(message, args: _*), length)\n    snackbar.getView.getLayoutParams match {\n      case params: FrameLayout.LayoutParams =>\n        val bottom = KitKat.ifSupportedThen(systemBarsTint.getNavigationBarHeight) getOrElse 0\n        params.setMargins(0, 0, 0, bottom)\n        snackbar.getView.setLayoutParams(params)\n      case _ =>\n    }\n    snackbar.setAction(resAction, new OnClickListener {\n      override def onClick(v: View): Unit = action()\n    })\n    snackbar.show()\n  }\n\n  def vStartDrag(\n      dragLauncherType: DragLauncherType,\n      shadow: DragShadowBuilder,\n      label: Option[String] = None,\n      text: Option[String] = None)(implicit contextWrapper: ContextWrapper): Tweak[View] =\n    Tweak[View] { view =>\n      val dragData =\n        ClipData.newPlainText(label getOrElse \"\", text getOrElse \"\")\n      view.startDrag(dragData, shadow, DragObject(shadow, dragLauncherType), 0)\n    }\n\n  def fblAddItems[T](items: Seq[T], onImageTweak: (T) => Tweak[ImageView], columns: Int = 5)(\n      implicit contextWrapper: ContextWrapper,\n      uiContext: UiContext[_]): Tweak[FlexboxLayout] = Tweak[FlexboxLayout] { view =>\n    val padding  = resGetDimensionPixelSize(R.dimen.padding_large)\n    val sizeIcon = resGetDimensionPixelSize(R.dimen.size_icon_item_collections_content) + (padding * 2)\n    val views = items map { item =>\n      (w[ImageView] <~\n        lp[FlexboxLayout](sizeIcon, sizeIcon) <~\n        vPadding(0, padding, 0, padding) <~\n        onImageTweak(item)).get\n    }\n    val numberEmptyViews = columns - (items.length % columns)\n    val emptyViews = 0 until numberEmptyViews map { _ =>\n      (w[ImageView] <~ lp[FlexboxLayout](sizeIcon, sizeIcon)).get\n    }\n    (view <~ vgAddViews(views ++ emptyViews)).run\n  }\n\n  def rvCloseAdapter() = Tweak[RecyclerView] { view =>\n    def safeClose(closeable: Closeable): Unit = Try(closeable.close()) match {\n      case Failure(ex) => printErrorMessage(ex)\n      case _           =>\n    }\n\n    Ui {\n      view.getAdapter match {\n        case a: Closeable => safeClose(a)\n        case _            =>\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class UiException(\n    message: String,\n    cause: Option[Throwable] = None,\n    recoverable: Boolean = false)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsUiExceptions {\n  implicit def uiExceptionConverter =\n    (t: Throwable) => UiException(t.getMessage, Option(t))\n}\n\ncase class ObserverException(\n    message: String,\n    cause: Option[Throwable] = None,\n    recoverable: Boolean = false)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsObserverExceptions {\n  implicit def observerExceptionConverter =\n    (t: Throwable) => ObserverException(t.getMessage, Option(t))\n}\n\ncase class JobException(\n    message: String,\n    cause: Option[Throwable] = None,\n    recoverable: Boolean = false)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsJobExceptions {\n  implicit def jobExceptionConverter =\n    (t: Throwable) => JobException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/Jobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons\n\nimport android.content.Intent\nimport android.content.Intent._\nimport android.graphics.{Bitmap, BitmapFactory}\nimport android.os.Bundle\nimport android.support.v7.app.AppCompatActivity\nimport cards.nine.app.commons.BroadcastDispatcher._\nimport cards.nine.app.commons.{ContextSupportProvider, Conversions}\nimport cards.nine.app.di.{Injector, InjectorImpl}\nimport cards.nine.app.ui.preferences.commons.Theme\nimport cards.nine.commons._\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.ShortcutCardType\nimport cards.nine.models.{Card, CardData, NineCardsTheme}\nimport cards.nine.process.cloud.Conversions._\nimport com.google.android.gms.common.api.GoogleApiClient\nimport macroid.ContextWrapper\n\nimport scala.util.Try\n\nclass Jobs(implicit contextWrapper: ContextWrapper)\n    extends ContextSupportProvider\n    with ImplicitsJobExceptions {\n\n  implicit lazy val di: Injector = new InjectorImpl\n\n  def themeFile = Theme.getThemeFile\n\n  def getThemeTask: TaskService[NineCardsTheme] =\n    di.themeProcess.getTheme(themeFile)\n\n  def sendBroadCastTask(broadAction: BroadAction): TaskService[Unit] =\n    TaskService(CatchAll[JobException](sendBroadCast(commandType, broadAction)))\n\n  def askBroadCastTask(broadAction: BroadAction): TaskService[Unit] =\n    TaskService(CatchAll[JobException](sendBroadCast(questionType, broadAction)))\n\n  private[this] def sendBroadCast(broadCastKeyType: String, broadAction: BroadAction): Unit = {\n    val intent = new Intent(broadAction.action)\n    intent.putExtra(keyType, broadCastKeyType)\n    broadAction.command foreach (d => intent.putExtra(keyCommand, d))\n    contextWrapper.bestAvailable.sendBroadcast(intent)\n  }\n\n  def withActivityTask(f: (AppCompatActivity => Unit)): TaskService[Unit] =\n    withActivity(activity => TaskService(CatchAll[JobException](f(activity))))\n\n  def withActivity(f: (AppCompatActivity => TaskService[Unit])): TaskService[Unit] =\n    contextWrapper.original.get match {\n      case Some(activity: AppCompatActivity) => f(activity)\n      case _                                 => TaskService.empty\n    }\n\n  def readIntValue(i: Intent, key: String): Option[Int] =\n    if (i.hasExtra(key)) Option(i.getIntExtra(key, 0)) else None\n\n  def readStringValue(i: Intent, key: String): Option[String] =\n    if (i.hasExtra(key)) Option(i.getStringExtra(key)) else None\n\n  def readArrayValue(i: Intent, key: String): Option[Array[String]] =\n    if (i.hasExtra(key)) Option(i.getStringArrayExtra(key)) else None\n\n}\n\ncase class BroadAction(action: String, command: Option[String] = None)\n\nclass SynchronizeDeviceJobs(implicit contextWrapper: ContextWrapper) extends Jobs {\n\n  def synchronizeDevice(client: GoogleApiClient): TaskService[Unit] = {\n    for {\n      collections <- di.collectionProcess.getCollections.resolveRight { seq =>\n        if (seq.isEmpty)\n          Left(JobException(\"Can't synchronize the device, no collections found\"))\n        else Right(seq)\n      }\n      moments  <- di.momentProcess.getMoments\n      widgets  <- di.widgetsProcess.getWidgets\n      dockApps <- di.deviceProcess.getDockApps\n      cloudStorageMoments = moments.filter(_.collectionId.isEmpty) map { moment =>\n        val widgetSeq = widgets.filter(_.momentId == moment.id) match {\n          case wSeq if wSeq.isEmpty => None\n          case wSeq                 => Some(wSeq)\n        }\n        toCloudStorageMoment(moment, widgetSeq)\n      }\n      firebaseToken <- di.externalServicesProcess.readFirebaseToken\n        .map(token => Option(token))\n        .resolveLeftTo(None)\n      savedDevice <- di.cloudStorageProcess.createOrUpdateActualCloudStorageDevice(\n        client = client,\n        collections = collections map (collection =>\n                                         toCloudStorageCollection(\n                                           collection,\n                                           collection.moment map (moment =>\n                                                                    widgets.filter(\n                                                                      _.momentId == moment.id)))),\n        moments = cloudStorageMoments,\n        dockApps = dockApps map toCloudStorageDockApp)\n      _ <- di.userProcess\n        .updateUserDevice(savedDevice.data.deviceName, savedDevice.cloudId, firebaseToken)\n    } yield ()\n  }\n\n}\n\nclass ShortcutJobs(implicit contextWrapper: ContextWrapper) extends Jobs with Conversions {\n\n  def addNewShortcut(collectionId: Int, data: Intent): TaskService[Option[Card]] = {\n\n    def getBitmapFromShortcutIntent(bundle: Bundle): TaskService[Bitmap] =\n      bundle match {\n        case b if b.containsKey(EXTRA_SHORTCUT_ICON) =>\n          TaskService(CatchAll[JobException](b.getParcelable[Bitmap](EXTRA_SHORTCUT_ICON)))\n        case b if b.containsKey(EXTRA_SHORTCUT_ICON_RESOURCE) =>\n          for {\n            resource <- TaskService(\n              CatchAll[JobException](\n                b.getParcelable[ShortcutIconResource](EXTRA_SHORTCUT_ICON_RESOURCE)))\n            bitmap <- di.deviceProcess.decodeShortcutIcon(resource)\n          } yield bitmap\n        case _ =>\n          TaskService.left(JobException(\"Can't find the icon in the provided bundle\"))\n      }\n\n    def createShortcut(\n        name: String,\n        shortcutIntent: Intent,\n        bitmap: Option[Bitmap]): TaskService[Option[Card]] =\n      for {\n        path <- bitmap map (di.deviceProcess\n          .saveShortcutIcon(_)\n          .map(Option(_))) getOrElse TaskService.right(None)\n        cardData = CardData(\n          term = name,\n          packageName = None,\n          cardType = ShortcutCardType,\n          intent = toNineCardIntent(shortcutIntent),\n          imagePath = path)\n        cards <- di.collectionProcess.addCards(collectionId, Seq(cardData))\n      } yield cards.headOption\n\n    Option(data) flatMap (i => Option(i.getExtras)) match {\n      case Some(b: Bundle)\n          if b.containsKey(EXTRA_SHORTCUT_NAME) && b.containsKey(EXTRA_SHORTCUT_INTENT) =>\n        val shortcutName   = b.getString(EXTRA_SHORTCUT_NAME)\n        val shortcutIntent = b.getParcelable[Intent](EXTRA_SHORTCUT_INTENT)\n\n        for {\n          maybeBitmap <- getBitmapFromShortcutIntent(b)\n            .resolveSides[Option[Bitmap]](b => Right(Option(b)), _ => Right(None))\n          maybeCard <- createShortcut(shortcutName, shortcutIntent, maybeBitmap).resolveRight {\n            case Some(card) => Right(Option(card))\n            case None =>\n              Left(JobException(s\"Error creating card for intent $shortcutName\"))\n          }\n        } yield maybeCard\n\n      case _ => TaskService.right(None)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/SnailsCommons.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons\n\nimport android.animation.ValueAnimator.AnimatorUpdateListener\nimport android.animation._\nimport android.annotation.SuppressLint\nimport android.view.View\nimport android.view.animation.{BaseInterpolator, DecelerateInterpolator}\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.preferences.commons.SpeedAnimations\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.extras.DeviceVersion.KitKat\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewTweaks._\nimport macroid.{ContextWrapper, Snail, Ui}\n\nimport scala.concurrent.Promise\n\nobject SnailsCommons {\n\n  val defaultDelay = 30\n\n  val noDelay = 0\n\n  def showFabMenu(implicit context: ContextWrapper): Snail[View] = {\n    vVisible + vScaleX(0) + vScaleY(0) ++\n      applyAnimation(scaleX = Option(1), scaleY = Option(1))\n  }\n\n  def hideFabMenu(implicit context: ContextWrapper): Snail[View] =\n    applyAnimation(scaleX = Option(0), scaleY = Option(0)) + vGone\n\n  def animFabMenuItem(position: Option[Int])(implicit context: ContextWrapper): Snail[View] = {\n    val translationY = resGetDimensionPixelSize(R.dimen.padding_default)\n    vVisible + vTranslationY(translationY) ++\n      applyAnimation(y = Option(0), startDelay = Option(calculateDelay(position)))\n  }\n\n  def animFabMenuTitleItem(position: Option[Int])(implicit context: ContextWrapper): Snail[View] =\n    vVisible + vAlpha(0) ++ applyAnimation(\n      alpha = Option(1),\n      startDelay = Option(calculateDelay(position)))\n\n  def animFabMenuIconItem(position: Option[Int])(implicit context: ContextWrapper): Snail[View] = {\n    val size = resGetDimensionPixelSize(R.dimen.size_fab_menu_item)\n    vVisible + vAlpha(0) + vScaleX(0) + vScaleY(0) + vPivotX(size / 2) + vPivotY(size) ++\n      applyAnimation(\n        alpha = Option(1),\n        scaleX = Option(1),\n        scaleY = Option(1),\n        startDelay = Option(calculateDelay(position)))\n  }\n\n  def applyFadeIn(duration: Option[Long] = None)(implicit context: ContextWrapper): Snail[View] =\n    vVisible + vAlpha(0) ++ applyAnimation(alpha = Some(1), duration = duration)\n\n  def applyFadeOut(duration: Option[Long] = None)(implicit context: ContextWrapper): Snail[View] =\n    applyAnimation(alpha = Some(0), duration = duration) + vInvisible + vAlpha(1)\n\n  @SuppressLint(Array(\"NewApi\"))\n  def applyAnimation(\n      startDelay: Option[Long] = None,\n      x: Option[Float] = None,\n      y: Option[Float] = None,\n      xBy: Option[Float] = None,\n      yBy: Option[Float] = None,\n      alpha: Option[Float] = None,\n      scaleX: Option[Float] = None,\n      scaleY: Option[Float] = None,\n      interpolator: Option[BaseInterpolator] = Option(new DecelerateInterpolator()),\n      duration: Option[Long] = None,\n      onUpdate: (Float) => Ui[_] = (_) => Ui.nop)(implicit context: ContextWrapper): Snail[View] =\n    Snail[View] { view =>\n      view.clearAnimation()\n      view.setRunningAnimation(true)\n      view.setLayerType(View.LAYER_TYPE_HARDWARE, javaNull)\n      val animPromise = Promise[Unit]()\n\n      val animator = view.animate.setListener(new AnimatorListenerAdapter {\n        override def onAnimationEnd(animation: Animator): Unit = {\n          super.onAnimationEnd(animation)\n          view.setLayerType(View.LAYER_TYPE_NONE, javaNull)\n          view.setRunningAnimation(false)\n          animPromise.trySuccess(())\n        }\n      })\n      KitKat.ifSupportedThen(animator.setUpdateListener(new AnimatorUpdateListener {\n        override def onAnimationUpdate(animation: ValueAnimator): Unit =\n          onUpdate(animation.getAnimatedFraction).run\n      }))\n      animator.setDuration(duration getOrElse SpeedAnimations.getDuration)\n      interpolator foreach animator.setInterpolator\n      startDelay foreach animator.setStartDelay\n      x foreach animator.translationX\n      y foreach animator.translationY\n      xBy foreach animator.translationXBy\n      yBy foreach animator.translationYBy\n      alpha foreach animator.alpha\n      scaleX foreach animator.scaleX\n      scaleY foreach animator.scaleY\n      animator.start()\n\n      animPromise.future\n    }\n\n  private[this] def calculateDelay(position: Option[Int]): Int =\n    position match {\n      case Some(p) => defaultDelay * p\n      case _       => noDelay\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/SystemBarsTint.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons\n\nimport android.annotation.SuppressLint\nimport android.graphics.Color\nimport android.view.View\nimport macroid.extras.DeviceVersion.{Lollipop, Marshmallow}\nimport cards.nine.utils.SystemBarTintManager\nimport macroid.{ActivityContextWrapper, Ui}\n\nclass SystemBarsTint(implicit activityContextWrapper: ActivityContextWrapper) {\n\n  private[this] lazy val systemBarTintManager = new SystemBarTintManager(\n    activityContextWrapper.getOriginal)\n\n  def initAllSystemBarsTint(): Ui[_] =\n    Ui(Lollipop ifNotSupportedThen {\n      systemBarTintManager.setStatusBarTintEnabled(true)\n      systemBarTintManager.setNavigationBarTintEnabled(true)\n    })\n\n  def initSystemStatusBarTint(): Ui[_] =\n    Ui(Lollipop ifNotSupportedThen {\n      systemBarTintManager.setStatusBarTintEnabled(true)\n    })\n\n  def initSystemNavigationBarTint(): Ui[_] =\n    Ui(Lollipop ifNotSupportedThen {\n      systemBarTintManager.setNavigationBarTintEnabled(true)\n    })\n\n  def updateStatusToBlack(): Ui[_] = updateStatusColor(Color.BLACK)\n\n  def updateStatusToTransparent(): Ui[_] = updateStatusColor(Color.TRANSPARENT)\n\n  @SuppressLint(Array(\"NewApi\"))\n  def updateStatusColor(color: Int): Ui[_] =\n    Ui {\n      Lollipop ifSupportedThen {\n        activityContextWrapper.getOriginal.getWindow.setStatusBarColor(color)\n      } getOrElse {\n        systemBarTintManager.setStatusBarTintColor(color)\n      }\n    }\n\n  def updateNavigationToBlack(): Ui[_] = updateNavigationColor(Color.BLACK)\n\n  def updateNavigationToTransparent(): Ui[_] =\n    updateNavigationColor(Color.TRANSPARENT)\n\n  @SuppressLint(Array(\"NewApi\"))\n  def updateNavigationColor(color: Int): Ui[_] =\n    Ui {\n      Lollipop ifSupportedThen {\n        activityContextWrapper.getOriginal.getWindow.setNavigationBarColor(color)\n      } getOrElse {\n        systemBarTintManager.setNavigationBarTintColor(color)\n      }\n    }\n\n  def lightStatusBar(): Ui[_] =\n    Ui(\n      Marshmallow ifSupportedThen\n        activityContextWrapper.getOriginal.getWindow.getDecorView\n          .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR))\n\n  def defaultStatusBar(): Ui[_] =\n    Ui(\n      Marshmallow ifSupportedThen\n        activityContextWrapper.getOriginal.getWindow.getDecorView.setSystemUiVisibility(0))\n\n  def hasNavigationBar = systemBarTintManager.getConfig.hasNavigationBar\n\n  def getNavigationBarHeight =\n    systemBarTintManager.getConfig.getNavigationBarHeight\n\n  def getStatusBarHeight = systemBarTintManager.getConfig.getStatusBarHeight\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/UiContext.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons\n\nimport android.app.{Activity, Application}\nimport android.content.Context\nimport android.support.v4.app.Fragment\n\nsealed trait UiContext[T] {\n  val value: T\n}\n\ncase class ApplicationUiContext(value: Application) extends UiContext[Application]\n\ncase class ActivityUiContext(value: Activity) extends UiContext[Activity]\n\ncase class FragmentUiContext(value: Fragment) extends UiContext[Fragment]\n\ncase class GenericUiContext(value: Context) extends UiContext[Context]\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/UiExtensions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Bundle\nimport android.support.v4.app.ActivityOptionsCompat\nimport macroid.{ActivityContextWrapper, ContextWrapper, Ui}\n\nimport scala.annotation.tailrec\nimport scala.ref.WeakReference\nimport scala.util.{Failure, Try}\n\ntrait UiExtensions {\n\n  @tailrec\n  private[this] def getData[T](\n      bundles: Seq[Bundle],\n      conversor: (Bundle, String) => T,\n      key: String,\n      default: T): T =\n    bundles match {\n      case Nil                                   => default\n      case Seq(h, t @ _ *) if h.containsKey(key) => conversor(h, key)\n      case Seq(h, t @ _ *)                       => getData(t, conversor, key, default)\n    }\n\n  def getInt(bundles: Seq[Bundle], key: String, default: Int) =\n    getData(flat(bundles), (b, k) => b.getInt(k), key, default)\n\n  def getString(bundles: Seq[Bundle], key: String, default: String) =\n    getData(flat(bundles), (b, k) => b.getString(k), key, default)\n\n  def getBoolean(bundles: Seq[Bundle], key: String, default: Boolean) =\n    getData(flat(bundles), (b, k) => b.getBoolean(k), key, default)\n\n  def getSeqString(bundles: Seq[Bundle], key: String, default: Seq[String]) =\n    getData(flat(bundles), (b, k) => b.getStringArray(k).toSeq, key, default)\n\n  def getSerialize[T](bundles: Seq[Bundle], key: String, default: T) =\n    getData(flat(bundles), (b, k) => b.getSerializable(k).asInstanceOf[T], key, default)\n\n  private[this] def flat(bundles: Seq[Bundle]): Seq[Bundle] =\n    bundles flatMap (b => Option(b))\n\n}\n\nobject SafeUi {\n\n  def uiStartIntent(intent: Intent)(implicit c: ActivityContextWrapper): Ui[Unit] =\n    startIntent(_.startActivity(intent))\n\n  def uiOpenUrlIntent(url: String)(implicit c: ContextWrapper): Ui[Unit] = {\n    val intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url))\n    Ui {\n      Try(c.bestAvailable.startActivity(intent)) match {\n        case Failure(e) => AppLog.printErrorMessage(e)\n        case _          =>\n      }\n    }\n  }\n\n  def uiStartIntentWithOptions(intent: Intent, options: ActivityOptionsCompat)(\n      implicit c: ActivityContextWrapper): Ui[Unit] =\n    startIntent(_.startActivity(intent, options.toBundle))\n\n  def uiStartIntentForResult(intent: Intent, requestCode: Int)(\n      implicit c: ActivityContextWrapper): Ui[Unit] =\n    startIntent(_.startActivityForResult(intent, requestCode))\n\n  def uiStartServiceIntent(intent: Intent)(implicit c: ActivityContextWrapper): Ui[Unit] =\n    Ui {\n      Try(c.bestAvailable.startService(intent)) match {\n        case Failure(e) => AppLog.printErrorMessage(e)\n        case _          =>\n      }\n    }\n\n  private[this] def startIntent(f: (Activity) => Unit)(\n      implicit c: ActivityContextWrapper): Ui[Unit] =\n    Ui {\n      Try(c.original.get foreach f) match {\n        case Failure(e) => AppLog.printErrorMessage(e)\n        case _          =>\n      }\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/action_filters/AppsActionFilter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.action_filters\n\nsealed trait AppsActionFilter {\n  val action: String\n}\n\ncase object AppInstalledActionFilter extends AppsActionFilter {\n  override val action: String = \"app-installed-action-filter\"\n}\n\ncase object AppUninstalledActionFilter extends AppsActionFilter {\n  override val action: String = \"app-uninstalled-action-filter\"\n}\n\ncase object AppUpdatedActionFilter extends AppsActionFilter {\n  override val action: String = \"app-updated-action-filter\"\n}\n\nobject AppsActionFilter {\n\n  val cases = Seq(AppInstalledActionFilter, AppUninstalledActionFilter, AppUpdatedActionFilter)\n\n  def apply(action: String): Option[AppsActionFilter] =\n    cases find (_.action == action)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/action_filters/CollectionsActionFilter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.action_filters\n\nsealed trait CollectionsActionFilter {\n  val action: String\n}\n\ncase object CollectionAddedActionFilter extends CollectionsActionFilter {\n  override val action: String = \"collections-added-action-filter\"\n}\n\nobject CollectionsActionFilter {\n\n  val cases = Seq(CollectionAddedActionFilter)\n\n  def apply(action: String): Option[CollectionsActionFilter] =\n    cases find (_.action == action)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/action_filters/MomentsActionFilter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.action_filters\n\nsealed trait MomentsActionFilter {\n  val action: String\n}\n\ncase object MomentReloadedActionFilter extends MomentsActionFilter {\n  override val action: String = \"moments-reloaded-action-filter\"\n}\n\ncase object MomentConstrainsChangedActionFilter extends MomentsActionFilter {\n  override val action: String = \"moments-constrains-changed-action-filter\"\n}\n\ncase object MomentAddedOrRemovedActionFilter extends MomentsActionFilter {\n  override val action: String = \"moments-added-or-removed-action-filter\"\n}\n\ncase object MomentBestAvailableActionFilter extends MomentsActionFilter {\n  override val action: String = \"moments-best-available-action-filter\"\n}\n\ncase object MomentForceBestAvailableActionFilter extends MomentsActionFilter {\n  override val action: String = \"moments-force-best-available-action-filter\"\n}\n\nobject MomentsActionFilter {\n\n  val cases = Seq(\n    MomentReloadedActionFilter,\n    MomentConstrainsChangedActionFilter,\n    MomentAddedOrRemovedActionFilter,\n    MomentBestAvailableActionFilter,\n    MomentForceBestAvailableActionFilter)\n\n  def apply(action: String): Option[MomentsActionFilter] =\n    cases find (_.action == action)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/action_filters/SyncActionFilter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.action_filters\n\nsealed trait SyncActionFilter {\n  val action: String\n}\n\ncase object SyncStateActionFilter extends SyncActionFilter {\n  override val action: String = \"sync-state-action-filter\"\n}\n\ncase object SyncAskActionFilter extends SyncActionFilter {\n  override val action: String = \"sync-ask-action-filter\"\n}\n\ncase object SyncAnswerActionFilter extends SyncActionFilter {\n  override val action: String = \"sync-answer-action-filter\"\n}\n\nobject SyncActionFilter {\n\n  val cases =\n    Seq(SyncStateActionFilter, SyncAskActionFilter, SyncAnswerActionFilter)\n\n  def apply(action: String): SyncActionFilter =\n    cases find (_.action == action) getOrElse\n      (throw new IllegalArgumentException(s\"$action not found\"))\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/adapters/apps/AppsAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.adapters.apps\n\nimport java.io.Closeable\n\nimport android.support.v7.widget.{GridLayoutManager, RecyclerView}\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.components.layouts.FastScrollerListener\nimport cards.nine.app.ui.components.widgets.ScrollingLinearLayoutManager\nimport cards.nine.app.ui.preferences.commons.{FontSize, IconsSize}\nimport cards.nine.models.types.theme.DrawerTextColor\nimport cards.nine.models.{\n  ApplicationData,\n  EmptyIterableApps,\n  IterableApplicationData,\n  NineCardsTheme\n}\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\n\ncase class AppsAdapter(\n    var apps: IterableApplicationData,\n    clickListener: (ApplicationData) => Unit,\n    longClickListener: Option[(View, ApplicationData) => Unit])(\n    implicit val activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[AppsIterableHolder]\n    with FastScrollerListener\n    with Closeable {\n\n  val columnsLists = 4\n\n  val heightItem = resGetDimensionPixelSize(R.dimen.height_app_item)\n\n  override def getItemCount: Int = apps.count()\n\n  override def onBindViewHolder(vh: AppsIterableHolder, position: Int): Unit =\n    vh.bind(apps.moveToPosition(position)).run\n\n  override def onCreateViewHolder(parent: ViewGroup, i: Int): AppsIterableHolder = {\n    val view = LayoutInflater.from(parent.getContext).inflate(TR.layout.app_item, parent, false)\n    AppsIterableHolder(view, clickListener, longClickListener)\n  }\n\n  def getLayoutManager: GridLayoutManager =\n    new ScrollingLinearLayoutManager(columnsLists)\n\n  def swapIterator(iter: IterableApplicationData) = {\n    apps.close()\n    apps = iter\n    notifyDataSetChanged()\n  }\n\n  def clear() = {\n    apps.close()\n    apps = new EmptyIterableApps()\n    notifyDataSetChanged()\n  }\n\n  override def close() = apps.close()\n\n  override def getHeightAllRows = apps.count() / columnsLists * getHeightItem\n\n  override def getHeightItem: Int = heightItem\n\n  override def getColumns: Int = columnsLists\n}\n\ncase class AppsIterableHolder(\n    content: ViewGroup,\n    clickListener: (ApplicationData) => Unit,\n    longClickListener: Option[(View, ApplicationData) => Unit])(\n    implicit context: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView {\n\n  lazy val icon = Option(findView(TR.simple_item_icon))\n\n  lazy val name = Option(findView(TR.simple_item_name))\n\n  def bind(app: ApplicationData): Ui[_] =\n    (icon <~ vResize(IconsSize.getIconApp) <~ ivSrcByPackageName(Some(app.packageName), app.name)) ~\n      (name <~ tvSizeResource(FontSize.getSizeResource) <~ tvText(app.name) + tvColor(\n        theme.get(DrawerTextColor))) ~\n      (content <~\n        On.click {\n          Ui(clickListener(app))\n        } <~\n        (longClickListener map { listener =>\n          FuncOn.longClick { view: View =>\n            icon foreach (listener(_, app))\n            Ui(true)\n          }\n        } getOrElse Tweak.blank))\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/adapters/apps/AppsSelectionAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.adapters.apps\n\nimport java.io.Closeable\n\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.OvalShape\nimport android.support.v7.widget.{GridLayoutManager, RecyclerView}\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport cards.nine.app.ui.commons.dialogs.apps.AppsFragment._\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.app.ui.components.layouts.FastScrollerListener\nimport cards.nine.app.ui.components.widgets.ScrollingLinearLayoutManager\nimport cards.nine.app.ui.preferences.commons.{FontSize, IconsSize}\nimport cards.nine.models.types.theme.{\n  DrawerBackgroundColor,\n  DrawerTabsBackgroundColor,\n  DrawerTextColor\n}\nimport cards.nine.models.{\n  ApplicationData,\n  EmptyIterableApps,\n  IterableApplicationData,\n  NineCardsTheme\n}\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\n\ncase class AppsSelectionAdapter(\n    var apps: IterableApplicationData,\n    clickListener: (ApplicationData) => Unit)(\n    implicit val activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[AppsSelectedIterableHolder]\n    with FastScrollerListener\n    with Closeable {\n\n  val columnsLists = 4\n\n  val heightItem = resGetDimensionPixelSize(R.dimen.height_app_item)\n\n  override def getItemCount: Int = apps.count()\n\n  override def onBindViewHolder(vh: AppsSelectedIterableHolder, position: Int): Unit =\n    vh.bind(apps.moveToPosition(position)).run\n\n  override def onCreateViewHolder(parent: ViewGroup, i: Int): AppsSelectedIterableHolder = {\n    val view =\n      LayoutInflater.from(parent.getContext).inflate(TR.layout.app_select_item, parent, false)\n    AppsSelectedIterableHolder(view, clickListener)\n  }\n\n  def getLayoutManager: GridLayoutManager =\n    new ScrollingLinearLayoutManager(columnsLists)\n\n  def swapIterator(iter: IterableApplicationData) = {\n    apps.close()\n    apps = iter\n    notifyDataSetChanged()\n  }\n\n  def clear() = {\n    apps.close()\n    apps = new EmptyIterableApps()\n    notifyDataSetChanged()\n  }\n\n  override def close() = apps.close()\n\n  override def getHeightAllRows = apps.count() / columnsLists * getHeightItem\n\n  override def getHeightItem: Int = heightItem\n\n  override def getColumns: Int = columnsLists\n}\n\ncase class AppsSelectedIterableHolder(\n    content: ViewGroup,\n    clickListener: (ApplicationData) => Unit)(\n    implicit context: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView {\n\n  lazy val icon = findView(TR.simple_item_icon)\n\n  lazy val name = findView(TR.simple_item_name)\n\n  lazy val item = findView(TR.app_item_content)\n\n  lazy val selectedIconContent = findView(TR.app_selected_content)\n\n  lazy val selectedIcon = findView(TR.app_selected)\n\n  lazy val selectedColor = resGetColor(R.color.checkbox_selected)\n\n  def selectedDrawable(color: Int) = {\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.getPaint.setColor(color)\n    drawable\n  }\n\n  val iconSelectedDrawable = PathMorphDrawable(\n    defaultIcon = IconTypes.CHECK,\n    defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_thin),\n    padding = resGetDimensionPixelSize(R.dimen.padding_select_icon))\n\n  (selectedIcon <~ ivSrc(iconSelectedDrawable) <~ vBackground(selectedDrawable(selectedColor))).run\n\n  def bind(app: ApplicationData): Ui[_] = {\n    val appSelected = appStatuses.selectedPackages.contains(app.packageName)\n    (icon <~ vResize(IconsSize.getIconApp) <~ ivSrcByPackageName(Some(app.packageName), app.name)) ~\n      (name <~ tvSizeResource(FontSize.getSizeResource) <~ tvText(app.name) + tvColor(\n        theme.get(DrawerTextColor))) ~\n      (selectedIconContent <~\n        (if (appSelected) vVisible else vGone) <~\n        vBackground(selectedDrawable(theme.get(DrawerBackgroundColor)))) ~\n      (item <~ (if (appSelected)\n                  vBackgroundColor(theme.get(DrawerTabsBackgroundColor))\n                else vBlankBackground)) ~\n      (content <~ On.click(Ui(clickListener(app))))\n  }\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/adapters/contacts/ContactsAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.adapters.contacts\n\nimport java.io.Closeable\n\nimport android.support.v7.widget.{LinearLayoutManager, RecyclerView}\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.components.layouts.FastScrollerListener\nimport cards.nine.app.ui.components.widgets.ScrollingLinearLayoutManager\nimport cards.nine.app.ui.preferences.commons.FontSize\nimport cards.nine.models.{Contact, IterableContacts, NineCardsTheme}\nimport cards.nine.models.types.theme.DrawerTextColor\nimport macroid.extras.DeviceVersion.Lollipop\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\ncase class ContactsAdapter(\n    var contacts: IterableContacts,\n    clickListener: (Contact) => Unit,\n    longClickListener: Option[(View, Contact) => Unit])(\n    implicit val activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[ContactsIterableHolder]\n    with FastScrollerListener\n    with Closeable {\n\n  val columnsLists = 1\n\n  val heightItem = resGetDimensionPixelSize(R.dimen.height_contact_item)\n\n  override def getItemCount: Int = contacts.count()\n\n  override def onBindViewHolder(vh: ContactsIterableHolder, position: Int): Unit =\n    vh.bind(contacts.moveToPosition(position), position).run\n\n  override def onCreateViewHolder(parent: ViewGroup, i: Int): ContactsIterableHolder = {\n    val view =\n      LayoutInflater.from(parent.getContext).inflate(TR.layout.contact_item, parent, false)\n    ContactsIterableHolder(view, clickListener, longClickListener)\n  }\n\n  def getLayoutManager: LinearLayoutManager =\n    new ScrollingLinearLayoutManager(columnsLists)\n\n  def swapIterator(iter: IterableContacts) = {\n    contacts.close()\n    contacts = iter\n    notifyDataSetChanged()\n  }\n\n  override def close() = contacts.close()\n\n  override def getHeightAllRows: Int =\n    contacts.count() / columnsLists * getHeightItem\n\n  override def getHeightItem: Int = heightItem\n\n  override def getColumns: Int = columnsLists\n\n}\n\ncase class ContactsIterableHolder(\n    content: View,\n    clickListener: (Contact) => Unit,\n    longClickListener: Option[(View, Contact) => Unit])(\n    implicit context: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView {\n\n  lazy val icon = Option(findView(TR.contact_item_icon))\n\n  lazy val name = Option(findView(TR.contact_item_name))\n\n  lazy val favorite = Option(findView(TR.contact_item_favorite))\n\n  (icon <~ (Lollipop ifSupportedThen vCircleOutlineProvider() getOrElse Tweak.blank)).run\n\n  def bind(contact: Contact, position: Int): Ui[_] = {\n    val contactName = Option(contact.name) getOrElse resGetString(R.string.unnamed)\n    (icon <~ ivUriContact(contact.photoUri, contactName, circular = true)) ~\n      (name <~ tvSizeResource(FontSize.getContactSizeResource) <~ tvText(contactName) <~ tvColor(\n        theme.get(DrawerTextColor))) ~\n      (favorite <~ (if (contact.favorite) vVisible else vGone)) ~\n      (content <~\n        On.click {\n          Ui(clickListener(contact))\n        } <~\n        (longClickListener map { listener =>\n          FuncOn.longClick { view: View =>\n            icon foreach (listener(_, contact))\n            Ui(true)\n          }\n        } getOrElse Tweak.blank))\n  }\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/adapters/contacts/LastCallsAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.adapters.contacts\n\nimport java.util.Date\n\nimport android.support.v7.widget.{LinearLayoutManager, RecyclerView}\nimport android.view.View.OnClickListener\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport android.widget.ImageView\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.layouts.FastScrollerListener\nimport cards.nine.app.ui.components.widgets.ScrollingLinearLayoutManager\nimport cards.nine.app.ui.preferences.commons.FontSize\nimport cards.nine.models.types.theme.DrawerTextColor\nimport cards.nine.models.{LastCallsContact, NineCardsTheme}\nimport cards.nine.models.types._\nimport macroid.extras.DeviceVersion.Lollipop\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\nimport org.ocpsoft.prettytime.PrettyTime\n\ncase class LastCallsAdapter(\n    contacts: Seq[LastCallsContact],\n    clickListener: (LastCallsContact) => Unit)(\n    implicit val activityContext: ActivityContextWrapper,\n    implicit val uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[LastCallsContactHolder]\n    with FastScrollerListener {\n\n  val columnsLists = 1\n\n  val heightItem = resGetDimensionPixelSize(R.dimen.height_contact_item)\n\n  override def getItemCount: Int = contacts.length\n\n  override def onBindViewHolder(vh: LastCallsContactHolder, position: Int): Unit =\n    vh.bind(contacts(position), position).run\n\n  override def onCreateViewHolder(parent: ViewGroup, i: Int): LastCallsContactHolder = {\n    val view =\n      LayoutInflater.from(parent.getContext).inflate(TR.layout.last_call_item, parent, false)\n    view.setOnClickListener(new OnClickListener {\n      override def onClick(v: View): Unit =\n        v.getPosition foreach (tag => clickListener(contacts(tag)))\n    })\n    LastCallsContactHolder(view)\n  }\n\n  def getLayoutManager: LinearLayoutManager =\n    new ScrollingLinearLayoutManager(columnsLists)\n\n  override def getHeightAllRows: Int =\n    contacts.length / columnsLists * getHeightItem\n\n  override def getHeightItem: Int = heightItem\n\n  override def getColumns: Int = columnsLists\n}\n\ncase class LastCallsContactHolder(content: View)(\n    implicit context: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView {\n\n  val maxCalls = 3\n\n  lazy val icon = Option(findView(TR.last_call_item_icon))\n\n  lazy val name = Option(findView(TR.last_call_item_name))\n\n  lazy val hour = Option(findView(TR.last_call_item_hour))\n\n  lazy val callTypes = Option(findView(TR.last_call_item_types))\n\n  (icon <~ (Lollipop ifSupportedThen vCircleOutlineProvider() getOrElse Tweak.blank)).run\n\n  def bind(contact: LastCallsContact, position: Int): Ui[_] = {\n    val date  = new Date(contact.lastCallDate)\n    val time  = new PrettyTime().format(date)\n    val color = theme.get(DrawerTextColor)\n    (icon <~ ivUriContact(contact.photoUri getOrElse \"\", contact.title, circular = true)) ~\n      (name <~ tvSizeResource(FontSize.getContactSizeResource) <~ tvText(contact.title) <~ tvColor(\n        color)) ~\n      (hour <~ tvSizeResource(FontSize.getSizeResource) <~ tvText(time) <~ tvColor(color)) ~\n      (callTypes <~ addCallTypesView(contact.calls take maxCalls map (_.callType))) ~\n      (content <~ vSetPosition(position))\n  }\n\n  private[this] def addCallTypesView(callTypes: Seq[CallType])(\n      implicit context: ActivityContextWrapper) = {\n    val padding = resGetDimensionPixelSize(R.dimen.padding_small)\n    val callViews = callTypes map { ct =>\n      (w[ImageView] <~ ivSrc(ct match {\n        case IncomingType => R.drawable.icon_call_incoming\n        case MissedType   => R.drawable.icon_call_missed\n        case _            => R.drawable.icon_call_outgoing\n      }) <~ vPadding(paddingRight = padding)).get\n    }\n    vgRemoveAllViews + vgAddViews(callViews)\n  }\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/adapters/search/SearchAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.adapters.search\n\nimport android.support.v7.widget.{GridLayoutManager, RecyclerView}\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport macroid.extras.ViewTweaks._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.app.ui.commons.styles.CommonStyles\nimport cards.nine.app.ui.commons.ops.DrawableOps._\nimport cards.nine.app.ui.components.layouts.FastScrollerListener\nimport cards.nine.app.ui.components.widgets.ScrollingLinearLayoutManager\nimport cards.nine.app.ui.preferences.commons.{FontSize, IconsSize}\nimport cards.nine.models.{NineCardsTheme, NotCategorizedPackage}\nimport cards.nine.models.types.theme.{DrawerIconColor, DrawerTextColor}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nclass SearchAdapter(\n    apps: Seq[NotCategorizedPackage],\n    clickListener: (NotCategorizedPackage) => Unit)(\n    implicit val activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[SearchViewHolder]\n    with FastScrollerListener {\n\n  val columnsLists = 1\n\n  val heightItem = resGetDimensionPixelSize(R.dimen.height_search_item)\n\n  override def getItemCount: Int = apps.length\n\n  override def onBindViewHolder(vh: SearchViewHolder, position: Int): Unit =\n    vh.bind(apps(position)).run\n\n  override def onCreateViewHolder(parent: ViewGroup, i: Int): SearchViewHolder = {\n    val view = LayoutInflater.from(parent.getContext).inflate(TR.layout.search_item, parent, false)\n    new SearchViewHolder(view, clickListener)\n  }\n\n  def getLayoutManager: GridLayoutManager =\n    new ScrollingLinearLayoutManager(columnsLists)\n\n  override def getHeightAllRows = apps.length / columnsLists * getHeightItem\n\n  override def getHeightItem: Int = heightItem\n\n  override def getColumns: Int = columnsLists\n}\n\nclass SearchViewHolder(content: ViewGroup, clickListener: (NotCategorizedPackage) => Unit)(\n    implicit context: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView\n    with CommonStyles {\n\n  lazy val icon = findView(TR.search_item_icon)\n\n  lazy val name = findView(TR.search_item_name)\n\n  lazy val stars = findView(TR.search_item_stars)\n\n  lazy val downloads = findView(TR.search_item_downloads)\n\n  lazy val price = findView(TR.search_item_price)\n\n  def bind(app: NotCategorizedPackage): Ui[_] = {\n    val alphaColor = theme.get(DrawerTextColor).alpha(subtitleAlpha)\n    val downloadIcon =\n      resGetDrawable(R.drawable.icon_download).colorize(theme.get(DrawerIconColor))\n    (icon <~\n      vResize(IconsSize.getIconApp) <~\n      (app.icon map ivUri getOrElse Tweak.blank)) ~\n      (name <~\n        tvSizeResource(FontSize.getTitleSizeResource) <~\n        tvText(app.title) <~\n        tvColor(theme.get(DrawerTextColor))) ~\n      (stars <~\n        ivSrc(getStarDrawable(app.stars)) <~\n        tivColor(alphaColor)) ~\n      (downloads <~\n        tvSizeResource(FontSize.getSizeResource) <~\n        tvCompoundDrawablesWithIntrinsicBounds(left = Option(downloadIcon)) <~\n        tvText(app.downloads) <~\n        tvColor(alphaColor)) ~\n      (price <~\n        tvText(if (app.free) resGetString(R.string.free) else \"\") <~\n        tvSizeResource(FontSize.getSizeResource) <~\n        tvColor(alphaColor)) ~\n      (content <~\n        On.click {\n          Ui(clickListener(app))\n        })\n  }\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/adapters/sharedcollections/SharedCollectionItem.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.adapters.sharedcollections\n\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.OvalShape\nimport android.view.{View, ViewGroup}\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.ops.SharedCollectionOps._\nimport cards.nine.app.ui.commons.styles.{CollectionCardsStyles, CommonStyles}\nimport cards.nine.models.types._\nimport cards.nine.models.{NineCardsTheme, SharedCollection, SharedCollectionPackage}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\ntrait SharedCollectionItem extends CollectionCardsStyles with CommonStyles with TypedFindView {\n\n  implicit val context: ActivityContextWrapper\n\n  implicit val uiContext: UiContext[_]\n\n  def content: ViewGroup\n\n  lazy val root = findView(TR.public_collections_item_layout)\n\n  lazy val iconContent = findView(TR.public_collections_item_content)\n\n  lazy val icon = findView(TR.public_collections_item_icon)\n\n  lazy val name = findView(TR.public_collections_item_name)\n\n  lazy val author = findView(TR.public_collections_item_author)\n\n  lazy val downloads = findView(TR.public_collections_item_downloads)\n\n  lazy val subscriptions = findView(TR.public_collections_item_subscriptions)\n\n  lazy val appsIcons = findView(TR.public_collections_item_apps)\n\n  lazy val addCollection = findView(TR.public_collections_item_add_collection)\n\n  lazy val shareCollection = findView(TR.public_collections_item_share_collection)\n\n  lazy val line = findView(TR.public_collections_item_line)\n\n  lazy val background = new ShapeDrawable(new OvalShape)\n\n  def initialize()(implicit theme: NineCardsTheme): Ui[Any] = {\n    (root <~ cardRootStyle) ~\n      (iconContent <~ vBackground(background)) ~\n      (name <~ titleTextStyle) ~\n      (line <~ vBackgroundColor(theme.getLineColor)) ~\n      (author <~ subtitleTextStyle) ~\n      (downloads <~ leftDrawableTextStyle(R.drawable.icon_collection_downloads) <~ subtitleTextStyle) ~\n      (subscriptions <~ leftDrawableTextStyle(R.drawable.icon_collection_subscriptions) <~ subtitleTextStyle) ~\n      (addCollection <~ buttonStyle) ~\n      (shareCollection <~ ivSrc(tintDrawable(R.drawable.icon_dialog_collection_share)))\n  }\n\n  def bind(collection: SharedCollection, onAddCollection: => Unit, onShareCollection: => Unit)(\n      implicit theme: NineCardsTheme): Ui[Any] = {\n\n    def addCollectionTweak() = collection.locallyAdded match {\n      case Some(true) =>\n        tvText(R.string.alreadyAddedCollection) +\n          tvAllCaps(false) + tvItalicLight + vEnabled(false)\n      case _ =>\n        tvText(R.string.addMyCollection) +\n          tvAllCaps(true) + tvNormalMedium + vEnabled(true)\n    }\n\n    background.getPaint.setColor(theme.getRandomIndexColor)\n    val apps = collection.resolvedPackages\n    (icon <~ ivSrc(collection.getIconCollectionDetail)) ~\n      (appsIcons <~\n        vgRemoveAllViews <~\n        fblAddItems(apps, (item: SharedCollectionPackage) => {\n          ivUri(item.icon)\n        })) ~\n      (name <~ tvText(collection.name)) ~\n      (author <~ tvText(collection.author)) ~\n      (subscriptions <~\n        (collection.subscriptions match {\n          case Some(number) =>\n            vVisible + tvText(resGetString(R.string.subscriptions_number, number.toString))\n          case _ => vGone\n        })) ~\n      (downloads <~ tvText(s\"${collection.views}\")) ~\n      (addCollection <~ addCollectionTweak() <~ On.click(\n        (addCollection <~ vEnabled(false)) ~ Ui(onAddCollection))) ~\n      (shareCollection <~ On.click(Ui(onShareCollection)))\n  }\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/adapters/sharedcollections/SharedCollectionsAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.adapters.sharedcollections\n\nimport android.support.v7.widget.{LinearLayoutManager, RecyclerView}\nimport android.view.{LayoutInflater, ViewGroup}\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.models.SharedCollection\nimport cards.nine.models.NineCardsTheme\nimport com.fortysevendeg.ninecardslauncher.TR\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport macroid.ActivityContextWrapper\n\ncase class SharedCollectionsAdapter(\n    sharedCollections: Seq[SharedCollection],\n    onAddCollection: (SharedCollection) => Unit,\n    onShareCollection: (SharedCollection) => Unit)(\n    implicit activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[ViewHolderSharedCollectionsLayoutAdapter] {\n\n  override def onCreateViewHolder(\n      parent: ViewGroup,\n      viewType: Int): ViewHolderSharedCollectionsLayoutAdapter = {\n    val view = LayoutInflater\n      .from(parent.getContext)\n      .inflate(TR.layout.public_collections_item, parent, false)\n    ViewHolderSharedCollectionsLayoutAdapter(view)\n  }\n\n  override def getItemCount: Int = sharedCollections.size\n\n  override def onBindViewHolder(\n      viewHolder: ViewHolderSharedCollectionsLayoutAdapter,\n      position: Int): Unit = {\n    val publicCollection = sharedCollections(position)\n    viewHolder\n      .bind(\n        publicCollection,\n        onAddCollection(publicCollection),\n        onShareCollection(publicCollection))\n      .run\n  }\n\n  def getLayoutManager = new LinearLayoutManager(activityContext.application)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/adapters/sharedcollections/ViewHolderSharedCollectionsLayoutAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.adapters.sharedcollections\n\nimport android.support.v7.widget.RecyclerView\nimport android.view.ViewGroup\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.models.NineCardsTheme\nimport macroid._\n\ncase class ViewHolderSharedCollectionsLayoutAdapter(content: ViewGroup)(\n    implicit val context: ActivityContextWrapper,\n    val uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with SharedCollectionItem {\n\n  initialize().run\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/BaseActionFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs\n\nimport android.app.Dialog\nimport android.support.design.widget.BottomSheetDialogFragment\nimport android.support.v4.app.Fragment\nimport android.view.{LayoutInflater, View}\nimport android.widget.FrameLayout\nimport cards.nine.app.commons.ContextSupportProvider\nimport cards.nine.app.di.{Injector, InjectorImpl}\nimport cards.nine.app.ui.commons.AppUtils._\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.{FragmentUiContext, UiContext, UiExtensions}\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.app.ui.preferences.commons.Theme\nimport cards.nine.commons._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.models._\nimport cards.nine.models.types.theme.{DrawerBackgroundColor, DrawerTextColor, PrimaryColor}\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ProgressBarTweaks._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\n\nimport scala.language.postfixOps\n\ntrait BaseActionFragment\n    extends BottomSheetDialogFragment\n    with TypedFindView\n    with ContextSupportProvider\n    with UiExtensions\n    with Contexts[Fragment] {\n\n  implicit lazy val di: Injector = new InjectorImpl\n\n  implicit lazy val uiContext: UiContext[Fragment] = FragmentUiContext(this)\n\n  implicit lazy val theme: NineCardsTheme =\n    di.themeProcess.getTheme(Theme.getThemeFile).resolveNow match {\n      case Right(t) => t\n      case _        => getDefaultTheme\n    }\n\n  private[this] lazy val defaultColor = theme.get(PrimaryColor)\n\n  override protected def findViewById(id: Int): View =\n    rootView map (_.findViewById(id)) orNull\n\n  protected lazy val colorPrimary =\n    getInt(Seq(getArguments), BaseActionFragment.colorPrimary, defaultColor)\n\n  protected lazy val backgroundColor = theme.get(DrawerBackgroundColor)\n\n  protected lazy val toolbar = Option(findView(TR.actions_toolbar))\n\n  protected lazy val loading = Option(findView(TR.action_loading))\n\n  protected lazy val loadingText = Option(findView(TR.action_loading_text))\n\n  protected lazy val loadingBar = Option(findView(TR.action_loading_bar))\n\n  protected lazy val content = Option(findView(TR.action_content_layout))\n\n  protected lazy val rootContent = Option(findView(TR.action_content_root))\n\n  protected lazy val fab = Option(findView(TR.action_content_fab))\n\n  protected lazy val errorContent = Option(findView(TR.actions_content_error_layout))\n\n  protected lazy val errorMessage = Option(findView(TR.actions_content_error_message))\n\n  protected lazy val errorIcon = Option(findView(TR.actions_content_error_icon))\n\n  protected lazy val errorButton = Option(findView(TR.actions_content_error_button))\n\n  protected var rootView: Option[FrameLayout] = None\n\n  def getLayoutId: Int\n\n  def useFab: Boolean = false\n\n  override def getTheme: Int = R.style.AppThemeDialog\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n\n    def fabAnimation =\n      vVisible + vScaleX(0) + vScaleY(0) ++ applyAnimation(scaleX = Option(1), scaleY = Option(1))\n\n    val baseView = LayoutInflater\n      .from(getActivity)\n      .inflate(R.layout.base_action_fragment, javaNull, false)\n      .asInstanceOf[FrameLayout]\n    val layout =\n      LayoutInflater.from(getActivity).inflate(getLayoutId, javaNull)\n    rootView = Option(baseView)\n    ((content <~ vgAddView(layout)) ~\n      (loadingBar <~ pbColor(colorPrimary)) ~\n      (errorIcon <~ tivColor(colorPrimary)) ~\n      (errorContent <~ vGone) ~\n      (errorMessage <~ tvColor(theme.get(DrawerTextColor).alpha(0.8f))) ~\n      (errorButton <~ vBackgroundTint(colorPrimary)) ~\n      (rootContent <~ vBackgroundColor(backgroundColor)) ~\n      (if (useFab) fab <~ fabAnimation else Ui.nop)).run\n\n    dialog.setContentView(baseView)\n  }\n\n  def unreveal(): Ui[Any] = Ui(dismissAllowingStateLoss())\n\n  def showMessageInScreen(message: Int, error: Boolean, action: => Unit): Ui[_] =\n    (loading <~ vGone) ~\n      (errorIcon <~ ivSrc(if (error) R.drawable.placeholder_error\n      else R.drawable.placeholder_empty)) ~\n      (errorMessage <~ text(message)) ~\n      (errorButton <~ On.click {\n        action\n        hideError\n      }) ~\n      (errorContent <~ vVisible)\n\n  def hideError: Ui[_] = errorContent <~ vGone\n\n}\n\nobject BaseActionFragment {\n  val packages     = \"packages\"\n  val colorPrimary = \"color_primary\"\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/Styles.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs\n\nimport android.support.design.widget.FloatingActionButton\nimport android.support.v7.widget.RecyclerView\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.app.ui.components.layouts.tweaks.FastScrollerLayoutTweak._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.DrawerTabsBackgroundColor\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.extras.FloatingActionButtonTweaks._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.{ContextWrapper, Tweak}\n\ntrait Styles {\n\n  def recyclerStyle(implicit context: ContextWrapper): Tweak[RecyclerView] =\n    rvFixedSize\n\n  def fabButtonMenuStyle(color: Int)(\n      implicit context: ContextWrapper): Tweak[FloatingActionButton] = {\n    val iconFabButton = PathMorphDrawable(\n      defaultIcon = IconTypes.CHECK,\n      defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_default))\n    val darkColor = color.dark()\n    ivSrc(iconFabButton) +\n      fbaColor(color, darkColor)\n  }\n\n  def scrollableStyle(color: Int)(implicit context: ContextWrapper, theme: NineCardsTheme) =\n    fslColor(color, theme.get(DrawerTabsBackgroundColor))\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/addmoment/AddMomentAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.addmoment\n\nimport android.support.v7.widget.{LinearLayoutManager, RecyclerView}\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.ops.NineCardsMomentOps._\nimport cards.nine.app.ui.commons.styles.CommonStyles\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.NineCardsMoment\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.TextViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nclass AddMomentAdapter(moments: Seq[NineCardsMoment], onClick: (NineCardsMoment => Unit))(\n    implicit activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[ViewHolderAddMomentLayoutAdapter] {\n\n  override def onCreateViewHolder(\n      parent: ViewGroup,\n      viewType: Int): ViewHolderAddMomentLayoutAdapter = {\n    val view =\n      LayoutInflater.from(parent.getContext).inflate(TR.layout.add_moment_item, parent, false)\n    new ViewHolderAddMomentLayoutAdapter(view)\n  }\n\n  override def getItemCount: Int = moments.size\n\n  override def onBindViewHolder(\n      viewHolder: ViewHolderAddMomentLayoutAdapter,\n      position: Int): Unit =\n    viewHolder.bind(moments(position), onClick).run\n\n  def getLayoutManager = new LinearLayoutManager(activityContext.application)\n\n}\n\nclass ViewHolderAddMomentLayoutAdapter(content: ViewGroup)(\n    implicit context: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView\n    with CommonStyles {\n\n  val appsByRow = 5\n\n  lazy val icon = findView(TR.add_moment_icon)\n\n  lazy val name = findView(TR.add_moment_name)\n\n  lazy val description = findView(TR.add_moment_description)\n\n  ((icon <~ iconMomentStyle) ~\n    (name <~ titleTextStyle) ~\n    (description <~ subtitleTextStyle)).run\n\n  def bind(moment: NineCardsMoment, onClick: (NineCardsMoment => Unit)): Ui[_] = {\n    (content <~ On.click(Ui(onClick(moment)))) ~\n      (icon <~ ivSrc(moment.getIconCollectionDetail)) ~\n      (name <~ tvText(moment.getName)) ~\n      (description <~ tvText(moment.getDescription))\n  }\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/addmoment/AddMomentDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.addmoment\n\nimport cards.nine.models.types.NineCardsMoment\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\n\ntrait AddMomentDOM { self: TypedFindView =>\n\n  lazy val recycler = findView(TR.actions_recycler)\n\n}\n\ntrait AddMomentListener {\n\n  def loadMoments(): Unit\n\n  def addMoment(moment: NineCardsMoment): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/addmoment/AddMomentFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.addmoment\n\nimport android.app.Dialog\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.models.types.NineCardsMoment\nimport cards.nine.models.types.theme.CardLayoutBackgroundColor\nimport com.fortysevendeg.ninecardslauncher.R\n\nclass AddMomentFragment\n    extends BaseActionFragment\n    with AddMomentDOM\n    with AddMomentUiActions\n    with AddMomentListener\n    with AppNineCardsIntentConversions { self =>\n\n  lazy val momentJobs = new AddMomentJobs(self)\n\n  override def getLayoutId: Int = R.layout.list_action_fragment\n\n  override protected lazy val backgroundColor: Int =\n    theme.get(CardLayoutBackgroundColor)\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n    momentJobs.initialize().resolveAsync()\n  }\n\n  override def loadMoments(): Unit =\n    momentJobs.loadMoments().resolveServiceOr(_ => showErrorLoadingCollectionInScreen())\n\n  override def addMoment(moment: NineCardsMoment): Unit =\n    momentJobs.addMoment(moment).resolveServiceOr(_ => showErrorSavingCollectionInScreen())\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/addmoment/AddMomentItemDecoration.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.addmoment\n\nimport android.graphics.{Canvas, Paint}\nimport android.support.v7.widget.RecyclerView\nimport android.support.v7.widget.RecyclerView.State\nimport cards.nine.models.NineCardsTheme\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nclass AddMomentItemDecoration(implicit theme: NineCardsTheme, contextWrapper: ContextWrapper)\n    extends RecyclerView.ItemDecoration {\n\n  val paint: Paint = {\n    val paint = new Paint\n    paint.setAntiAlias(true)\n    paint.setColor(theme.getLineColor)\n    paint.setStrokeWidth(resGetDimensionPixelSize(R.dimen.stroke_thin))\n    paint.setStyle(Paint.Style.STROKE)\n    paint\n  }\n\n  override def onDraw(c: Canvas, recyclerView: RecyclerView, state: State): Unit = {\n    super.onDraw(c, recyclerView, state)\n    (0 to recyclerView.getChildCount flatMap (i => Option(recyclerView.getChildAt(i)))) foreach {\n      view =>\n        c.drawLine(view.getLeft, view.getBottom, view.getRight, view.getBottom, paint)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/addmoment/AddMomentJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.addmoment\n\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.ui.commons.action_filters.MomentAddedOrRemovedActionFilter\nimport cards.nine.app.ui.commons.{BroadAction, Jobs}\nimport cards.nine.commons.services.TaskService.{TaskService, _}\nimport cards.nine.models.Moment.MomentTimeSlotOps\nimport cards.nine.models.types._\nimport cards.nine.models.{Moment, MomentData}\nimport macroid.ActivityContextWrapper\n\nclass AddMomentJobs(actions: AddMomentUiActions)(implicit contextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with Conversions {\n\n  def initialize(): TaskService[Unit] =\n    for {\n      _ <- actions.initialize()\n      _ <- loadMoments()\n    } yield ()\n\n  def loadMoments(): TaskService[Unit] = {\n\n    def getMomentNotUsed(currentMoments: Seq[Moment]): Seq[NineCardsMoment] =\n      NineCardsMoment.moments filterNot (moment =>\n                                           currentMoments map (_.momentType) contains moment)\n\n    for {\n      _       <- actions.showLoading()\n      moments <- di.momentProcess.getMoments\n      momentNotUsed = getMomentNotUsed(moments)\n      _ <- if (momentNotUsed.isEmpty) {\n        actions.showEmptyMessageInScreen()\n      } else {\n        actions.addMoments(momentNotUsed)\n      }\n    } yield ()\n  }\n\n  def addMoment(nineCardsMoment: NineCardsMoment): TaskService[Unit] = {\n    val moment = MomentData(\n      collectionId = None,\n      timeslot = nineCardsMoment.toMomentTimeSlot,\n      wifi = Seq.empty,\n      bluetooth = Seq.empty,\n      headphone = false,\n      momentType = nineCardsMoment)\n    for {\n      _ <- di.trackEventProcess.addMoment(nineCardsMoment.name)\n      _ <- di.momentProcess.saveMoments(Seq(moment))\n      _ <- sendBroadCastTask(BroadAction(MomentAddedOrRemovedActionFilter.action))\n      _ <- actions.close()\n    } yield ()\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/addmoment/AddMomentUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.addmoment\n\nimport cards.nine.app.ui.commons.dialogs.{BaseActionFragment, Styles}\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.layouts.tweaks.DialogToolbarTweaks._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.types.NineCardsMoment\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.ViewTweaks._\n\ntrait AddMomentUiActions extends Styles {\n\n  self: BaseActionFragment with AddMomentDOM with AddMomentListener =>\n\n  def initialize(): TaskService[Unit] =\n    ((toolbar <~\n      dtbInit(colorPrimary) <~\n      dtbChangeText(R.string.addMoment) <~\n      dtbNavigationOnClickListener((_) => unreveal())) ~\n      (recycler <~ recyclerStyle <~ rvAddItemDecoration(new AddMomentItemDecoration))).toService()\n\n  def addMoments(moments: Seq[NineCardsMoment]): TaskService[Unit] = {\n    val adapter = new AddMomentAdapter(moments, addMoment)\n    ((recycler <~\n      vVisible <~\n      rvLayoutManager(adapter.getLayoutManager) <~\n      rvAdapter(adapter)) ~\n      (loading <~ vGone)).toService()\n  }\n\n  def showLoading(): TaskService[Unit] =\n    ((loading <~ vVisible) ~ (recycler <~ vGone)).toService()\n\n  def showEmptyMessageInScreen(): TaskService[Unit] =\n    showMessageInScreen(R.string.emptyAddMoment, error = false, loadMoments()).toService()\n\n  def showErrorLoadingCollectionInScreen(): TaskService[Unit] =\n    showMessageInScreen(R.string.errorLoadingAddMoment, error = true, loadMoments()).toService()\n\n  def showErrorSavingCollectionInScreen(): TaskService[Unit] =\n    showMessageInScreen(R.string.errorSavingAddMoment, error = true, loadMoments()).toService()\n\n  def close(): TaskService[Unit] = unreveal().toService()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/apps/AppsDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.apps\n\nimport android.support.v4.app.Fragment\nimport cards.nine.app.ui.commons.adapters.apps.AppsSelectionAdapter\nimport cards.nine.models.{ApplicationData, NotCategorizedPackage}\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\nimport macroid.Contexts\n\ntrait AppsDOM { finder: TypedFindView with Contexts[Fragment] =>\n\n  val searchingGooglePlayKey = \"searching-google-play-key\"\n\n  lazy val recycler = findView(TR.apps_actions_recycler)\n\n  lazy val selectedAppsContent = findView(TR.selected_apps_content)\n\n  lazy val selectedApps = findView(TR.selected_apps)\n\n  lazy val appsMessage = findView(TR.apps_action_message)\n\n  def getAdapter: Option[AppsSelectionAdapter] =\n    Option(recycler.getAdapter) match {\n      case Some(a: AppsSelectionAdapter) => Some(a)\n      case _                             => None\n    }\n\n}\n\ntrait AppsUiListener {\n\n  def loadApps(): Unit\n\n  def loadFilteredApps(keyword: String): Unit\n\n  def loadSearch(query: String): Unit\n\n  def launchGooglePlay(app: NotCategorizedPackage): Unit\n\n  def updateSelectedApps(app: ApplicationData): Unit\n\n  def updateCollectionApps(): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/apps/AppsFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.apps\n\nimport android.app.Dialog\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.ui.commons.dialogs.apps.AppsFragment._\nimport cards.nine.app.ui.collections.jobs.{GroupCollectionsJobs, SingleCollectionJobs}\nimport cards.nine.app.ui.commons.UiExtensions\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{ApplicationData, NotCategorizedPackage}\nimport com.fortysevendeg.ninecardslauncher.R\n\nclass AppsFragment(\n    implicit groupCollectionsJobs: GroupCollectionsJobs,\n    singleCollectionJobs: Option[SingleCollectionJobs])\n    extends BaseActionFragment\n    with AppsUiActions\n    with AppsDOM\n    with AppsUiListener\n    with Conversions\n    with UiExtensions { self =>\n\n  lazy val appsJobs = AppsJobs(actions = self)\n\n  lazy val packages =\n    getSeqString(Seq(getArguments), BaseActionFragment.packages, Seq.empty[String]).toSet\n\n  override def useFab: Boolean = true\n\n  override def getLayoutId: Int = R.layout.list_action_apps_fragment\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n    appStatuses = appStatuses.copy(initialPackages = packages, selectedPackages = packages)\n    appsJobs.initialize(appStatuses.selectedPackages).resolveAsync()\n  }\n\n  override def onDestroy(): Unit = {\n    appsJobs.destroy().resolveAsync()\n    super.onDestroy()\n  }\n\n  override def loadApps(): Unit = {\n    appStatuses = appStatuses.copy(contentView = AppsView)\n    appsJobs.loadApps().resolveAsyncServiceOr(_ => appsJobs.showErrorLoadingApps())\n  }\n\n  override def loadFilteredApps(keyword: String): Unit =\n    appsJobs.loadAppsByKeyword(keyword).resolveAsyncServiceOr(_ => appsJobs.showErrorLoadingApps())\n\n  override def loadSearch(query: String): Unit = {\n    appStatuses = appStatuses.copy(contentView = GooglePlayView)\n    appsJobs.loadSearch(query).resolveAsyncServiceOr(_ => appsJobs.showErrorLoadingApps())\n  }\n\n  override def launchGooglePlay(app: NotCategorizedPackage): Unit =\n    (for {\n      _     <- appsJobs.launchGooglePlay(app.packageName)\n      cards <- groupCollectionsJobs.addCards(Seq(toCardData(app)))\n      _ <- singleCollectionJobs match {\n        case Some(job) => job.addCards(cards)\n        case _         => TaskService.empty\n      }\n      _ <- appsJobs.close()\n    } yield ()).resolveAsyncServiceOr(_ => appsJobs.showErrorLoadingApps())\n\n  override def updateSelectedApps(app: ApplicationData): Unit = {\n    appStatuses = appStatuses.update(app.packageName)\n    appsJobs\n      .updateSelectedApps(appStatuses.selectedPackages)\n      .resolveAsyncServiceOr(_ => appsJobs.showError())\n  }\n\n  override def updateCollectionApps(): Unit = {\n\n    def updateCards(): TaskService[Unit] =\n      for {\n        result <- appsJobs.getAddedAndRemovedApps\n        (cardsToAdd, cardsToRemove) = result\n        cardsRemoved <- groupCollectionsJobs.removeCardsByPackagesName(\n          cardsToRemove flatMap (_.packageName))\n        _ <- singleCollectionJobs match {\n          case Some(job) => job.removeCards(cardsRemoved)\n          case _         => TaskService.empty\n        }\n        cardsAdded <- groupCollectionsJobs.addCards(cardsToAdd)\n        _ <- singleCollectionJobs match {\n          case Some(job) => job.addCards(cardsAdded)\n          case _         => TaskService.empty\n        }\n      } yield ()\n\n    (for {\n      _ <- if (appStatuses.initialPackages == appStatuses.selectedPackages)\n        TaskService.empty\n      else updateCards()\n      _ <- appsJobs.close()\n    } yield ()).resolveAsyncServiceOr(_ => appsJobs.showError())\n\n  }\n\n}\n\nobject AppsFragment {\n\n  var appStatuses = AppsStatuses()\n\n  val categoryKey = \"category\"\n}\n\ncase class AppsStatuses(\n    initialPackages: Set[String] = Set.empty,\n    selectedPackages: Set[String] = Set.empty,\n    contentView: ContentView = AppsView) {\n\n  def update(packageName: String): AppsStatuses =\n    if (selectedPackages.contains(packageName))\n      copy(selectedPackages = selectedPackages - packageName)\n    else copy(selectedPackages = selectedPackages + packageName)\n\n}\n\nsealed trait ContentView\n\ncase object AppsView extends ContentView\n\ncase object GooglePlayView extends ContentView\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/apps/AppsJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.apps\n\nimport cards.nine.app.commons.{AppNineCardsIntentConversions, Conversions}\nimport cards.nine.app.ui.commons.dialogs.apps.AppsFragment._\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models._\nimport cards.nine.models.types._\nimport macroid.ActivityContextWrapper\n\ncase class AppsJobs(actions: AppsUiActions)(\n    implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with AppNineCardsIntentConversions\n    with Conversions {\n\n  def initialize(selectedApps: Set[String]): TaskService[Unit] =\n    for {\n      _ <- actions.initialize(selectedApps)\n      _ <- loadApps()\n    } yield ()\n\n  def destroy(): TaskService[Unit] = actions.destroy()\n\n  def loadSearch(query: String): TaskService[Unit] = {\n    for {\n      _      <- actions.showLoadingInGooglePlay()\n      result <- di.recommendationsProcess.searchApps(query)\n      _      <- actions.reloadSearch(result)\n    } yield ()\n  }\n\n  def loadApps(): TaskService[Unit] = {\n\n    def getLoadApps(order: GetAppOrder): TaskService[(IterableApplicationData, Seq[TermCounter])] =\n      for {\n        iterableApps <- di.deviceProcess.getIterableApps(order)\n        counters     <- di.deviceProcess.getTermCountersForApps(order)\n      } yield (iterableApps, counters)\n\n    for {\n      _    <- actions.showSelectedMessageAndFab()\n      _    <- actions.showLoading()\n      data <- getLoadApps(GetByName)\n      (apps, counters) = data\n      _ <- actions.showApps(apps, counters)\n    } yield ()\n  }\n\n  def loadAppsByKeyword(keyword: String): TaskService[Unit] =\n    for {\n      apps <- di.deviceProcess.getIterableAppsByKeyWord(keyword, GetByName)\n      _    <- actions.showApps(apps, Seq.empty)\n    } yield ()\n\n  def getAddedAndRemovedApps: TaskService[(Seq[CardData], Seq[CardData])] = {\n\n    val initialPackages  = appStatuses.initialPackages\n    val selectedPackages = appStatuses.selectedPackages\n\n    def getCardsFromPackages(\n        packageNames: Set[String],\n        apps: Seq[ApplicationData]): Seq[CardData] =\n      (packageNames flatMap { packageName =>\n        apps.find(_.packageName == packageName)\n      } map toCardData).toSeq\n\n    for {\n      allApps <- di.deviceProcess.getSavedApps(GetByName)\n    } yield {\n      val cardsToAdd =\n        getCardsFromPackages(selectedPackages.diff(initialPackages), allApps)\n      val cardsToRemove =\n        getCardsFromPackages(initialPackages.diff(selectedPackages), allApps)\n      (cardsToAdd, cardsToRemove)\n    }\n  }\n\n  def updateSelectedApps(packages: Set[String]): TaskService[Unit] =\n    actions.showUpdateSelectedApps(packages)\n\n  def launchGooglePlay(packageName: String): TaskService[Unit] =\n    di.launcherExecutorProcess.launchGooglePlay(packageName)\n\n  def showErrorLoadingApps(): TaskService[Unit] =\n    actions.showErrorLoadingAppsInScreen()\n\n  def showError(): TaskService[Unit] = actions.showError()\n\n  def close(): TaskService[Unit] = actions.close()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/apps/AppsUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.apps\n\nimport android.view.View\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.dialogs.apps.AppsFragment._\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.adapters.apps.AppsSelectionAdapter\nimport cards.nine.app.ui.commons.adapters.search.SearchAdapter\nimport cards.nine.app.ui.commons.dialogs.{BaseActionFragment, Styles}\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.styles.CommonStyles\nimport cards.nine.app.ui.components.commons.SelectedItemDecoration\nimport cards.nine.app.ui.components.drawables.IconTypes\nimport cards.nine.app.ui.components.layouts.tweaks.DialogToolbarTweaks._\nimport cards.nine.app.ui.preferences.commons.{AppDrawerSelectItemsInScroller, FontSize}\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models._\nimport cards.nine.models.types.DialogToolbarSearch\nimport cards.nine.models.types.theme.{\n  DrawerBackgroundColor,\n  DrawerTabsBackgroundColor,\n  DrawerTextColor\n}\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.DeviceVersion.Lollipop\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\n\ntrait AppsUiActions extends AppNineCardsIntentConversions with Styles with CommonStyles {\n\n  self: BaseActionFragment with AppsDOM with AppsUiListener =>\n\n  val resistance = 2.4f\n\n  def initialize(selectedAppsSeq: Set[String]): TaskService[Unit] = {\n    val selectItemsInScrolling = AppDrawerSelectItemsInScroller.readValue\n    ((toolbar <~\n      dtbInit(colorPrimary, DialogToolbarSearch) <~\n      dtbClickActionSearch((query) => loadSearch(query)) <~\n      dtbNavigationOnClickListener((_) => hideKeyboard ~ unreveal()) <~\n      dtbOnSearchTextChangedListener((text: String, start: Int, before: Int, count: Int) => {\n        (text, appStatuses.contentView) match {\n          case (\"\", _)       => loadApps()\n          case (t, AppsView) => loadFilteredApps(t)\n          case _             =>\n        }\n      })) ~\n      (fab <~\n        fabButtonMenuStyle(colorPrimary) <~\n        On.click(Ui(updateCollectionApps()))) ~\n      (selectedAppsContent <~\n        selectedAppsStyle <~\n        vBackgroundColor(theme.get(DrawerBackgroundColor))) ~\n      (selectedApps <~\n        subtitleTextStyle <~\n        vBackgroundColor(theme.get(DrawerTabsBackgroundColor)) <~\n        tvText(resGetString(R.string.selectedApps, selectedAppsSeq.size.toString))) ~\n      (appsMessage <~ tvSizeResource(FontSize.getSizeResource) <~ tvColor(\n        theme.get(DrawerTextColor))) ~\n      (recycler <~ recyclerStyle <~\n        (if (selectItemsInScrolling)\n           rvAddItemDecoration(new SelectedItemDecoration)\n         else Tweak.blank))).toService()\n  }\n\n  def showSelectedMessageAndFab(): TaskService[Unit] =\n    ((selectedApps <~ vVisible) ~\n      (fab <~ vVisible) ~\n      (toolbar <~\n        dtbSetIcon(IconTypes.CLOSE) <~\n        dtbNavigationOnClickListener((_) => hideKeyboard ~ unreveal()))).toService()\n\n  def showLoading(): TaskService[Unit] =\n    ((loading <~ vVisible) ~ (recycler <~ vGone)).toService()\n\n  def showError(): TaskService[Unit] = showGeneralError.toService()\n\n  def destroy(): TaskService[Unit] =\n    Ui {\n      getAdapter foreach (_.close())\n    }.toService()\n\n  def close(): TaskService[Unit] = (hideKeyboard ~ unreveal()).toService()\n\n  def showErrorLoadingAppsInScreen(): TaskService[Unit] =\n    showMessageInScreen(R.string.errorLoadingApps, error = true, loadApps()).toService()\n\n  def showApps(apps: IterableApplicationData, counters: Seq[TermCounter]): TaskService[Unit] =\n    if (apps.count() == 0) showSearchGooglePlayMessage().toService()\n    else\n      (hideMessage() ~ generateAppsSelectionAdapter(apps, counters, updateSelectedApps))\n        .toService()\n\n  def showUpdateSelectedApps(packages: Set[String]): TaskService[Unit] =\n    (Ui(getAdapter foreach (_.notifyDataSetChanged())) ~\n      (selectedApps <~\n        tvText(resGetString(R.string.selectedApps, packages.size.toString)))).toService()\n\n  def showLoadingInGooglePlay(): TaskService[Unit] =\n    showSearchingInGooglePlay().toService()\n\n  def reloadSearch(apps: Seq[NotCategorizedPackage]): TaskService[Unit] = {\n\n    def addSearch(\n        apps: Seq[NotCategorizedPackage],\n        clickListener: (NotCategorizedPackage) => Unit): Ui[Any] = {\n      val appsAdapter = new SearchAdapter(apps, clickListener)\n      recycler <~\n        rvCloseAdapter <~\n        vVisible <~\n        rvLayoutManager(appsAdapter.getLayoutManager) <~\n        rvAdapter(appsAdapter) <~\n        rvScrollToTop\n    }\n\n    if (apps.isEmpty) {\n      showAppsNotFoundInGooglePlay().toService()\n    } else {\n      (hideMessage() ~\n        addSearch(apps = apps, clickListener = launchGooglePlay)).toService()\n    }\n  }\n\n  private[this] def hideKeyboard: Ui[Any] =\n    toolbar <~ dtbHideKeyboardSearchText\n\n  private[this] def showSearchGooglePlayMessage(): Ui[Any] =\n    (appsMessage <~ tvText(R.string.apps_not_found) <~ vVisible) ~\n      (recycler <~ vGone)\n\n  private[this] def showSearchingInGooglePlay(): Ui[Any] =\n    (appsMessage <~ tvText(R.string.searching_in_google_play) <~ vVisible) ~\n      (toolbar <~\n        dtbSetIcon(IconTypes.BACK) <~\n        dtbNavigationOnClickListener((_) => (toolbar <~ dtbResetText) ~ Ui(loadApps()))) ~\n      (selectedApps <~ vGone) ~\n      (fab <~ vGone) ~\n      (recycler <~ vGone)\n\n  private[this] def showAppsNotFoundInGooglePlay(): Ui[Any] =\n    (appsMessage <~ tvText(R.string.apps_not_found_in_google_play) <~ vVisible) ~\n      (recycler <~ vGone)\n\n  private[this] def hideMessage(): Ui[Any] =\n    (appsMessage <~ vGone) ~ (recycler <~ vVisible)\n\n  private[this] def showData: Ui[_] =\n    (loading <~ vGone) ~ (recycler <~ vVisible)\n\n  private[this] def showGeneralError: Ui[_] =\n    rootContent <~ vSnackbarShort(R.string.contactUsError)\n\n  private[this] def generateAppsSelectionAdapter(\n      apps: IterableApplicationData,\n      counters: Seq[TermCounter],\n      clickListener: (ApplicationData) => Unit) = {\n    val adapter =\n      AppsSelectionAdapter(apps = apps, clickListener = clickListener)\n    showData ~\n      (recycler <~\n        rvLayoutManager(adapter.getLayoutManager) <~\n        rvAdapter(adapter))\n  }\n\n  private[this] def selectedAppsStyle: Tweak[View] =\n    Lollipop ifSupportedThen {\n      vElevation(resGetDimension(R.dimen.elevation_toolbar))\n    } getOrElse Tweak.blank\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/contacts/ContactsDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.contacts\n\nimport android.support.v4.app.DialogFragment\nimport android.support.v7.app.AppCompatActivity\nimport cards.nine.app.ui.commons.adapters.contacts.ContactsAdapter\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\nimport macroid.ActivityContextWrapper\n\ntrait ContactsDOM { finder: TypedFindView =>\n\n  val tagDialog = \"contact-dialog\"\n\n  lazy val recycler = findView(TR.actions_recycler)\n\n  def getAdapter: Option[ContactsAdapter] =\n    Option(recycler.getAdapter) match {\n      case Some(a: ContactsAdapter) => Some(a)\n      case _                        => None\n    }\n\n  // TODO We should move this call to NavigationProcess #826\n  def showDialog(dialog: DialogFragment)(\n      implicit activityContextWrapper: ActivityContextWrapper): Unit = {\n    activityContextWrapper.original.get match {\n      case Some(activity: AppCompatActivity) =>\n        val ft = activity.getSupportFragmentManager.beginTransaction()\n        Option(activity.getSupportFragmentManager.findFragmentByTag(tagDialog)) foreach ft.remove\n        ft.addToBackStack(javaNull)\n        dialog.show(ft, tagDialog)\n      case _ =>\n    }\n  }\n\n}\n\ntrait ContactsUiListener {\n\n  def loadContacts(): Unit\n\n  def loadContactsByKeyword(keyword: String): Unit\n\n  def showContact(lookupKey: String): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/contacts/ContactsFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.contacts\n\nimport android.app.{Activity, Dialog}\nimport android.content.Intent\nimport android.os.Bundle\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.collections.jobs.{GroupCollectionsJobs, SingleCollectionJobs}\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.{JobException, RequestCodes}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.CardData\nimport cards.nine.process.device.ContactPermissionException\nimport com.fortysevendeg.ninecardslauncher.R\n\nclass ContactsFragment(\n    implicit groupCollectionsJobs: GroupCollectionsJobs,\n    singleCollectionJobs: Option[SingleCollectionJobs])\n    extends BaseActionFragment\n    with ContactsUiActions\n    with ContactsDOM\n    with ContactsUiListener\n    with AppNineCardsIntentConversions { self =>\n\n  lazy val contactsJobs = new ContactsJobs(self)\n\n  override def getLayoutId: Int = R.layout.list_action_fragment\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n    contactsJobs.initialize().resolveAsyncServiceOr(e => onError(e))\n  }\n\n  override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit = {\n    super.onActivityResult(requestCode, resultCode, data)\n\n    def readExtras = Option(data) flatMap (d => Option(d.getExtras))\n\n    def readExtraProperty(extras: Bundle, extra: String): Option[AnyRef] =\n      if (extras.containsKey(ContactsFragment.addCardRequest)) {\n        Option(extras.get(ContactsFragment.addCardRequest))\n      } else {\n        None\n      }\n\n    (requestCode, resultCode) match {\n      case (RequestCodes.selectInfoContact, Activity.RESULT_OK) =>\n        val maybeRequest = readExtras flatMap { extras =>\n          readExtraProperty(extras, ContactsFragment.addCardRequest) match {\n            case Some(card: CardData) => Some(card)\n            case _                    => None\n          }\n        }\n        (for {\n          cards <- maybeRequest match {\n            case Some(request) => groupCollectionsJobs.addCards(Seq(request))\n            case _             => TaskService.left(JobException(\"Request not found\"))\n          }\n          _ <- singleCollectionJobs match {\n            case Some(job) => job.addCards(cards)\n            case _         => TaskService.empty\n          }\n          _ <- contactsJobs.close()\n        } yield ()).resolveAsyncServiceOr(_ => contactsJobs.showError())\n      case _ =>\n    }\n  }\n\n  override def onRequestPermissionsResult(\n      requestCode: Int,\n      permissions: Array[String],\n      grantResults: Array[Int]): Unit =\n    contactsJobs\n      .requestPermissionsResult(requestCode, permissions, grantResults)\n      .resolveAsyncServiceOr(e => onError(e))\n\n  override def onDestroy(): Unit = {\n    contactsJobs.destroy()\n    super.onDestroy()\n  }\n\n  override def loadContacts(): Unit =\n    contactsJobs.loadContacts().resolveAsyncServiceOr(e => onError(e))\n\n  def loadContactsByKeyword(keyword: String): Unit =\n    contactsJobs.loadContacts(Option(keyword)).resolveAsyncServiceOr(e => onError(e))\n\n  override def showContact(lookupKey: String): Unit =\n    contactsJobs.showContact(lookupKey).resolveAsyncServiceOr(_ => contactsJobs.showError())\n\n  private[this] def onError(e: Throwable) = e match {\n    case e: ContactPermissionException =>\n      contactsJobs.askForContactsPermission(RequestCodes.contactsPermission)\n    case _ => contactsJobs.showErrorLoadingContacts()\n  }\n\n}\n\nobject ContactsFragment {\n  val addCardRequest = \"add-card-request\"\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/contacts/ContactsJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.contacts\n\nimport cards.nine.app.permissions.PermissionChecker\nimport cards.nine.app.permissions.PermissionChecker.ReadContacts\nimport cards.nine.app.ui.commons.{Jobs, RequestCodes}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.AllContacts\nimport macroid.ActivityContextWrapper\n\nclass ContactsJobs(actions: ContactsUiActions)(\n    implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs {\n\n  val permissionChecker = new PermissionChecker\n\n  def initialize(): TaskService[Unit] =\n    for {\n      _ <- actions.initialize()\n      _ <- loadContacts()\n    } yield ()\n\n  def destroy(): TaskService[Unit] = actions.destroy()\n\n  def loadContacts(byKeyword: Option[String] = None): TaskService[Unit] =\n    for {\n      _ <- actions.showLoading()\n      contacts <- byKeyword match {\n        case Some(keyword) =>\n          di.deviceProcess.getIterableContactsByKeyWord(keyword)\n        case _ => di.deviceProcess.getIterableContacts(AllContacts)\n      }\n      _ <- actions.showContacts(contacts)\n    } yield ()\n\n  def askForContactsPermission(requestCode: Int): TaskService[Unit] =\n    actions.askForContactsPermission(requestCode)\n\n  def showContact(lookupKey: String): TaskService[Unit] =\n    for {\n      _       <- di.trackEventProcess.addContactByFab()\n      contact <- di.deviceProcess.getContact(lookupKey)\n      _       <- actions.showSelectContactDialog(contact)\n    } yield ()\n\n  def requestPermissionsResult(\n      requestCode: Int,\n      permissions: Array[String],\n      grantResults: Array[Int]): TaskService[Unit] =\n    if (requestCode == RequestCodes.contactsPermission) {\n      for {\n        result <- permissionChecker.readPermissionRequestResultTask(permissions, grantResults)\n        hasPermission = result.exists(_.hasPermission(ReadContacts))\n        _ <- if (hasPermission) loadContacts()\n        else actions.showErrorContactsPermission()\n      } yield ()\n    } else {\n      TaskService.empty\n    }\n\n  def showError(): TaskService[Unit] = actions.showError()\n\n  def showErrorLoadingContacts(): TaskService[Unit] =\n    actions.showErrorLoadingContactsInScreen()\n\n  def close(): TaskService[Unit] = actions.close()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/contacts/ContactsUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.contacts\n\nimport cards.nine.app.permissions.PermissionChecker.ReadContacts\nimport cards.nine.app.ui.commons.RequestCodes\nimport cards.nine.app.ui.commons.adapters.contacts.ContactsAdapter\nimport cards.nine.app.ui.commons.dialogs.{BaseActionFragment, Styles}\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.layouts.tweaks.DialogToolbarTweaks._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.{Contact, IterableContacts}\nimport cards.nine.models.types.DialogToolbarSearch\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.ViewTweaks._\n\ntrait ContactsUiActions extends Styles {\n\n  self: BaseActionFragment with ContactsDOM with ContactsUiListener =>\n\n  def initialize(): TaskService[Unit] =\n    ((toolbar <~\n      dtbInit(colorPrimary, DialogToolbarSearch) <~\n      dtbChangeText(R.string.allContacts) <~\n      dtbNavigationOnClickListener((_) => hideKeyboard ~ unreveal()) <~\n      dtbOnSearchTextChangedListener((text: String, start: Int, before: Int, count: Int) => {\n        loadContactsByKeyword(text)\n      })) ~\n      (recycler <~ recyclerStyle)).toService()\n\n  def showLoading(): TaskService[Unit] =\n    ((loading <~ vVisible) ~ (recycler <~ vGone) ~ hideError).toService()\n\n  def destroy(): TaskService[Unit] =\n    Ui {\n      getAdapter foreach (_.close())\n    }.toService()\n\n  def showContacts(contacts: IterableContacts): TaskService[Unit] =\n    ((getAdapter match {\n      case Some(adapter) =>\n        showData ~ Ui(adapter.swapIterator(contacts)) ~ (recycler <~ rvScrollToTop)\n      case _ =>\n        val adapter =\n          ContactsAdapter(contacts, contact => showContact(contact.lookupKey), None)\n        showData ~\n          (recycler <~\n            rvLayoutManager(adapter.getLayoutManager) <~\n            rvAdapter(adapter))\n    }) ~ (loading <~ vGone)).toService()\n\n  def askForContactsPermission(requestCode: Int): TaskService[Unit] =\n    Ui {\n      requestPermissions(Array(ReadContacts.value), requestCode)\n    }.toService()\n\n  def showError(): TaskService[Unit] = showGeneralError().toService()\n\n  def showErrorContactsPermission(): TaskService[Unit] =\n    ((recycler <~ vGone) ~\n      showMessageInScreen(R.string.errorContactsPermission, error = true, action = loadContacts()))\n      .toService()\n\n  def showErrorLoadingContactsInScreen(): TaskService[Unit] =\n    ((recycler <~ vGone) ~\n      showMessageInScreen(R.string.errorLoadingContacts, error = true, action = loadContacts()))\n      .toService()\n\n  def showSelectContactDialog(contact: Contact): TaskService[Unit] = {\n    val dialog = SelectInfoContactDialogFragment(contact)\n    dialog.setTargetFragment(this, RequestCodes.selectInfoContact)\n    Ui(showDialog(dialog)).toService()\n  }\n\n  def close(): TaskService[Unit] = unreveal().toService()\n\n  private[this] def showGeneralError(): Ui[Any] =\n    rootContent <~ vSnackbarShort(R.string.contactUsError)\n\n  private[this] def showData: Ui[Any] =\n    (loading <~ vGone) ~ (recycler <~ vVisible)\n\n  private[this] def hideKeyboard: Ui[Any] =\n    toolbar <~ dtbHideKeyboardSearchText\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/contacts/SelectInfoContactDialogFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.contacts\n\nimport android.app.{Activity, Dialog}\nimport android.content.Intent\nimport android.os.Bundle\nimport android.support.v4.app.DialogFragment\nimport android.support.v7.app.AlertDialog\nimport android.view.{LayoutInflater, View}\nimport android.widget.{LinearLayout, ScrollView}\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.models.types._\nimport cards.nine.models.types.theme.{\n  DrawerBackgroundColor,\n  DrawerIconColor,\n  DrawerTextColor,\n  PrimaryColor\n}\nimport cards.nine.models.{CardData, Contact, NineCardsTheme}\nimport macroid.extras.DeviceVersion.Lollipop\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nimport scala.annotation.tailrec\n\ncase class SelectInfoContactDialogFragment(contact: Contact)(\n    implicit contextWrapper: ContextWrapper,\n    activityContext: ActivityContextWrapper,\n    theme: NineCardsTheme,\n    uiContext: UiContext[_])\n    extends DialogFragment\n    with AppNineCardsIntentConversions {\n\n  val primaryColor = theme.get(PrimaryColor)\n\n  val textColor = theme.get(DrawerTextColor)\n\n  val iconColor = theme.get(DrawerIconColor)\n\n  val lineColor = theme.getLineColor\n\n  override def onCreateDialog(savedInstanceState: Bundle): Dialog = {\n    val scrollView = new ScrollView(getActivity)\n    val rootView   = new LinearLayout(getActivity)\n    rootView.setOrientation(LinearLayout.VERTICAL)\n\n    val views = contact.info map { info =>\n      generateHeaderView(contact.name, contact.photoUri) ++\n        generateGeneralInfoView(contact.lookupKey, contact.photoUri) ++\n        generatePhoneViews(\n          contact.lookupKey,\n          info.phones map (phone => (phone.number, phone.category)),\n          Seq.empty) ++\n        generateEmailViews(\n          contact.lookupKey,\n          info.emails map (email => (email.address, email.category)),\n          Seq.empty)\n    } getOrElse Seq.empty\n\n    ((rootView <~\n      vgAddViews(views) <~\n      vBackgroundColor(theme.get(DrawerBackgroundColor))) ~\n      (scrollView <~ vgAddView(rootView))).run\n\n    new AlertDialog.Builder(getActivity).setView(scrollView).create()\n  }\n\n  class HeaderView(name: String, avatarUrl: String)\n      extends LinearLayout(contextWrapper.bestAvailable)\n      with TypedFindView {\n\n    LayoutInflater.from(getActivity).inflate(R.layout.contact_info_header, this)\n\n    lazy val headerAvatar = findView(TR.contact_info_header_avatar)\n    lazy val headerName   = findView(TR.contact_info_header_name)\n\n    ((headerAvatar <~\n      ivUriContactInfo(avatarUrl, header = true) <~\n      vBackgroundColor(primaryColor)) ~\n      (headerName <~ tvText(name))).run\n  }\n\n  class GeneralInfoView(lookupKey: String, avatarUrl: String)\n      extends LinearLayout(contextWrapper.bestAvailable)\n      with TypedFindView {\n\n    LayoutInflater.from(getActivity).inflate(R.layout.contact_info_general_dialog, this)\n\n    lazy val generalContent = findView(TR.contact_dialog_general_content)\n    lazy val icon           = findView(TR.contact_dialog_general_icon)\n    lazy val generalInfo    = findView(TR.contact_dialog_general_info)\n    lazy val line           = findView(TR.contact_dialog_general_line)\n\n    ((icon <~\n      ivUriContactInfo(avatarUrl, header = false) <~\n      (Lollipop ifSupportedThen vCircleOutlineProvider() getOrElse Tweak.blank) <~\n      vBackgroundColor(primaryColor)) ~\n      (line <~ vBackgroundColor(lineColor)) ~\n      (generalInfo <~\n        tvColor(textColor) <~\n        tvText(getResources.getString(R.string.generalInfo))) ~\n      (generalContent <~ On.click(generateIntent(lookupKey, None, ContactCardType)))).run\n  }\n\n  class PhoneView(lookupKey: String, data: (String, PhoneCategory))\n      extends LinearLayout(contextWrapper.bestAvailable)\n      with TypedFindView {\n\n    val (phone, category) = data\n\n    val categoryName = category match {\n      case PhoneHome    => getResources.getString(R.string.phoneHome)\n      case PhoneWork    => getResources.getString(R.string.phoneWork)\n      case PhoneMobile  => getResources.getString(R.string.phoneMobile)\n      case PhoneMain    => getResources.getString(R.string.phoneMain)\n      case PhoneFaxWork => getResources.getString(R.string.phoneFaxWork)\n      case PhoneFaxHome => getResources.getString(R.string.phoneFaxHome)\n      case PhonePager   => getResources.getString(R.string.phonePager)\n      case PhoneOther   => getResources.getString(R.string.phoneOther)\n    }\n\n    LayoutInflater.from(getActivity).inflate(R.layout.contact_info_phone_dialog, this)\n\n    lazy val phoneContent  = findView(TR.contact_dialog_phone_content)\n    lazy val phoneNumber   = findView(TR.contact_dialog_phone_number)\n    lazy val phoneCategory = findView(TR.contact_dialog_phone_category)\n    lazy val phoneIcon     = findView(TR.contact_dialog_phone_icon)\n    lazy val phoneSms      = findView(TR.contact_dialog_sms_icon)\n    lazy val line          = findView(TR.contact_dialog_phone_line)\n\n    ((phoneNumber <~\n      tvColor(textColor) <~\n      tvText(phone)) ~\n      (line <~ vBackgroundColor(lineColor)) ~\n      (phoneCategory <~\n        tvColor(textColor) <~\n        tvText(categoryName)) ~\n      (phoneIcon <~ tivColor(iconColor)) ~\n      (phoneContent <~ On.click(generateIntent(lookupKey, Option(phone), PhoneCardType))) ~\n      (phoneSms <~ tivColor(iconColor) <~ On.click(\n        generateIntent(lookupKey, Option(phone), SmsCardType)))).run\n  }\n\n  class EmailView(lookupKey: String, data: (String, EmailCategory))\n      extends LinearLayout(contextWrapper.bestAvailable)\n      with TypedFindView {\n\n    val (email, category) = data\n\n    val categoryName = category match {\n      case EmailHome  => getResources.getString(R.string.emailHome)\n      case EmailWork  => getResources.getString(R.string.emailWork)\n      case EmailOther => getResources.getString(R.string.emailOther)\n    }\n\n    LayoutInflater.from(getActivity).inflate(R.layout.contact_info_email_dialog, this)\n\n    lazy val emailContent  = findView(TR.contact_dialog_email_content)\n    lazy val emailAddress  = findView(TR.contact_dialog_email_address)\n    lazy val emailCategory = findView(TR.contact_dialog_email_category)\n    lazy val emailIcon     = findView(TR.contact_dialog_email_icon)\n    lazy val line          = findView(TR.contact_dialog_email_line)\n\n    ((emailAddress <~\n      tvColor(textColor) <~\n      tvText(email)) ~\n      (line <~ vBackgroundColor(lineColor)) ~\n      (emailIcon <~ tivColor(iconColor)) ~\n      (emailCategory <~\n        tvColor(textColor) <~\n        tvText(categoryName)) ~\n      (emailContent <~ On.click(generateIntent(lookupKey, Option(email), EmailCardType)))).run\n  }\n\n  private[this] def generateHeaderView(name: String, avatarUrl: String): Seq[View] =\n    Seq(new HeaderView(name, avatarUrl))\n\n  private[this] def generateGeneralInfoView(lookupKey: String, avatarUrl: String): Seq[View] =\n    Seq(new GeneralInfoView(lookupKey, avatarUrl))\n\n  @tailrec\n  private[this] def generatePhoneViews(\n      lookupKey: String,\n      items: Seq[(String, PhoneCategory)],\n      acc: Seq[View]): Seq[View] =\n    items match {\n      case Nil => acc\n      case h :: t =>\n        val viewItem = new PhoneView(lookupKey, h)\n        val newAcc   = acc :+ viewItem\n        generatePhoneViews(lookupKey, t, newAcc)\n    }\n\n  @tailrec\n  private[this] def generateEmailViews(\n      lookupKey: String,\n      items: Seq[(String, EmailCategory)],\n      acc: Seq[View]): Seq[View] =\n    items match {\n      case Nil => acc\n      case h :: t =>\n        val viewItem = new EmailView(lookupKey, h)\n        val newAcc   = acc :+ viewItem\n        generateEmailViews(lookupKey, t, newAcc)\n    }\n\n  private[this] def generateIntent(\n      lookupKey: String,\n      maybeData: Option[String],\n      cardType: CardType): Ui[_] = Ui {\n    val (intent, lastCardType) = (cardType, maybeData) match {\n      case (EmailCardType, Some(data)) =>\n        (emailToNineCardIntent(Option(lookupKey), data), cardType)\n      case (SmsCardType, Some(data)) =>\n        (smsToNineCardIntent(Option(lookupKey), data), cardType)\n      case (PhoneCardType, Some(data)) =>\n        (phoneToNineCardIntent(Option(lookupKey), data), cardType)\n      case _ => (contactToNineCardIntent(lookupKey), ContactCardType)\n    }\n    val card = CardData(\n      term = contact.name,\n      packageName = None,\n      cardType = lastCardType,\n      intent = intent,\n      imagePath = Option(contact.photoUri))\n    val responseIntent = new Intent\n    responseIntent.putExtra(ContactsFragment.addCardRequest, card)\n    getTargetFragment.onActivityResult(getTargetRequestCode, Activity.RESULT_OK, responseIntent)\n    dismiss()\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/createoreditcollection/ColorDialogFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.createoreditcollection\n\nimport android.app.{Activity, Dialog}\nimport android.content.Intent\nimport android.graphics.Paint.Style\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.OvalShape\nimport android.os.Bundle\nimport android.support.v4.app.DialogFragment\nimport android.support.v7.app.AlertDialog\nimport android.view.ViewGroup.LayoutParams._\nimport android.view.{Gravity, LayoutInflater}\nimport android.widget.LinearLayout\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.DrawerBackgroundColor\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\ncase class ColorDialogFragment(\n    index: Int)(implicit contextWrapper: ContextWrapper, theme: NineCardsTheme)\n    extends DialogFragment\n    with AppNineCardsIntentConversions {\n\n  override def onCreateDialog(savedInstanceState: Bundle): Dialog = {\n    def createRow(from: Int, to: Int): LinearLayout = {\n      val layout = new LinearLayout(getActivity)\n      layout.setOrientation(LinearLayout.HORIZONTAL)\n      val params = new LinearLayout.LayoutParams(0, WRAP_CONTENT, 1)\n\n      val views = from to to map (i => new ItemView(i, select = index == i))\n      (layout <~ vgAddViews(views, params)).run\n      layout\n    }\n    val rootView = new LinearLayout(getActivity)\n    rootView.setOrientation(LinearLayout.VERTICAL)\n\n    val views = Seq(createRow(0, 2), createRow(3, 5), createRow(6, 8))\n\n    val params = new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)\n    params.gravity = Gravity.CENTER\n\n    (rootView <~ vBackgroundColor(theme.get(DrawerBackgroundColor)) <~ vgAddViews(views, params)).run\n\n    new AlertDialog.Builder(getActivity).setView(rootView).create()\n  }\n\n  class ItemView(index: Int, select: Boolean)\n      extends LinearLayout(contextWrapper.bestAvailable)\n      with TypedFindView {\n\n    LayoutInflater.from(getActivity).inflate(R.layout.color_info_item_dialog, this)\n\n    lazy val color = Option(findView(TR.color_info_image))\n\n    val icon = PathMorphDrawable(\n      defaultIcon = IconTypes.CHECK,\n      defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_large),\n      defaultColor = resGetColor(R.color.color_selected_color_dialog),\n      padding = resGetDimensionPixelSize(R.dimen.padding_large))\n\n    ((color <~\n      (if (select) ivSrc(icon) else Tweak.blank) <~\n      vBackground(getDrawable(index))) ~\n      (this <~\n        On.click {\n          Ui {\n            val responseIntent = new Intent\n            responseIntent.putExtra(CreateOrEditCollectionFragment.colorRequest, index)\n            getTargetFragment.onActivityResult(\n              getTargetRequestCode,\n              Activity.RESULT_OK,\n              responseIntent)\n            dismiss()\n          }\n        })).run\n  }\n\n  private[this] def getDrawable(index: Int) = {\n    val color    = theme.getIndexColor(index)\n    val size     = resGetDimensionPixelSize(R.dimen.size_icon_select_new_collection)\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.setIntrinsicHeight(size)\n    drawable.setIntrinsicWidth(size)\n    drawable.getPaint.setColor(color)\n    drawable.getPaint.setStyle(Style.FILL)\n    drawable.getPaint.setAntiAlias(true)\n    drawable\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/createoreditcollection/CreateOrEditCollectionDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.createoreditcollection\n\nimport cards.nine.models.Collection\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\n\ntrait CreateOrEditCollectionDOM { self: TypedFindView =>\n\n  lazy val name = findView(TR.new_collection_name)\n\n  lazy val collectionName = findView(TR.new_collection_name)\n\n  lazy val colorContent = findView(TR.new_collection_select_color_content)\n\n  lazy val colorImage = findView(TR.new_collection_select_color_image)\n\n  lazy val colorText = findView(TR.new_collection_select_color_text)\n\n  lazy val iconContent = findView(TR.new_collection_select_icon_content)\n\n  lazy val iconImage = findView(TR.new_collection_select_icon_image)\n\n  lazy val iconText = findView(TR.new_collection_select_icon_text)\n\n}\n\ntrait CreateOrEditCollectionListener {\n\n  def changeColor(maybeColor: Option[Int])\n\n  def changeIcon(maybeIcon: Option[String])\n\n  def saveCollection(\n      maybeName: Option[String],\n      maybeIcon: Option[String],\n      maybeIndex: Option[Int]): Unit\n\n  def editCollection(\n      collection: Collection,\n      maybeName: Option[String],\n      maybeIcon: Option[String],\n      maybeIndex: Option[Int]): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/createoreditcollection/CreateOrEditCollectionFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.createoreditcollection\n\nimport android.app.{Activity, Dialog}\nimport android.content.Intent\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.RequestCodes\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.launcher.jobs.LauncherJobs\nimport cards.nine.commons.javaNull\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.Collection\nimport com.fortysevendeg.ninecardslauncher.R\n\nclass CreateOrEditCollectionFragment(implicit launcherJobs: LauncherJobs)\n    extends BaseActionFragment\n    with CreateOrEditCollectionDOM\n    with CreateOrEditCollectionUiActions\n    with CreateOrEditCollectionListener\n    with AppNineCardsIntentConversions { self =>\n\n  lazy val maybeCollectionId = Option(\n    getString(Seq(getArguments), CreateOrEditCollectionFragment.collectionId, javaNull))\n\n  lazy val collectionJobs = new CreateOrEditCollectionJobs(self)\n\n  override def getLayoutId: Int = R.layout.new_collection\n\n  override def useFab: Boolean = true\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n    collectionJobs.initialize(maybeCollectionId).resolveServiceOr(_ => showMessageContactUsError)\n  }\n\n  override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit = {\n    super.onActivityResult(requestCode, resultCode, data)\n    (requestCode, resultCode) match {\n      case (RequestCodes.selectInfoIcon, Activity.RESULT_OK) =>\n        val maybeIcon = Option(data) flatMap (d => Option(d.getExtras)) map {\n          case extras if extras.containsKey(CreateOrEditCollectionFragment.iconRequest) =>\n            Some(extras.getString(CreateOrEditCollectionFragment.iconRequest))\n          case _ => None\n        } getOrElse None\n        collectionJobs.updateIcon(maybeIcon).resolveAsyncServiceOr(_ => showMessageContactUsError)\n      case (RequestCodes.selectInfoColor, Activity.RESULT_OK) =>\n        val maybeIndexColor = Option(data) flatMap (d => Option(d.getExtras)) map {\n          case extras if extras.containsKey(CreateOrEditCollectionFragment.colorRequest) =>\n            Some(extras.getInt(CreateOrEditCollectionFragment.colorRequest))\n          case _ => None\n        } getOrElse None\n        collectionJobs\n          .updateColor(maybeIndexColor)\n          .resolveAsyncServiceOr(_ => showMessageContactUsError)\n      case _ =>\n    }\n  }\n\n  override def changeColor(maybeColor: Option[Int]): Unit =\n    collectionJobs.changeColor(maybeColor).resolveAsyncServiceOr(_ => showMessageContactUsError)\n\n  override def changeIcon(maybeIcon: Option[String]): Unit =\n    collectionJobs.changeIcon(maybeIcon).resolveAsyncServiceOr(_ => showMessageContactUsError)\n\n  override def saveCollection(\n      maybeName: Option[String],\n      maybeIcon: Option[String],\n      maybeIndex: Option[Int]): Unit =\n    ((maybeName, maybeIcon, maybeIndex) match {\n      case (Some(nameCollection), Some(icon), Some(themedColorIndex)) =>\n        for {\n          collection <- collectionJobs.saveCollection(nameCollection, icon, themedColorIndex)\n          _          <- launcherJobs.addCollection(collection)\n        } yield ()\n      case _ => showMessageFormFieldError\n    }).resolveServiceOr(_ => showMessageContactUsError)\n\n  override def editCollection(\n      collection: Collection,\n      maybeName: Option[String],\n      maybeIcon: Option[String],\n      maybeIndex: Option[Int]): Unit = {\n    ((maybeName, maybeIcon, maybeIndex) match {\n      case (Some(nameCollection), Some(icon), Some(themedColorIndex)) =>\n        for {\n          collection <- collectionJobs.editCollection(\n            collection,\n            nameCollection,\n            icon,\n            themedColorIndex)\n          _ <- launcherJobs.updateCollection(collection)\n        } yield ()\n      case _ => showMessageFormFieldError\n    }).resolveServiceOr(_ => showMessageContactUsError)\n  }\n}\n\nobject CreateOrEditCollectionFragment {\n  val iconRequest  = \"icon-request\"\n  val colorRequest = \"color-request\"\n  val collectionId = \"collectionId\"\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/createoreditcollection/CreateOrEditCollectionJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.createoreditcollection\n\nimport cards.nine.app.ui.commons.{JobException, Jobs}\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.{TaskService, _}\nimport cards.nine.models.{Collection, CollectionData}\nimport cards.nine.models.types.FreeCollectionType\nimport macroid.ActivityContextWrapper\n\nclass CreateOrEditCollectionJobs(actions: CreateOrEditCollectionUiActions)(\n    implicit contextWrapper: ActivityContextWrapper)\n    extends Jobs {\n\n  def initialize(maybeCollectionId: Option[String]): TaskService[Unit] = {\n\n    def editCollection(collectionId: Int) =\n      for {\n        collection <- di.collectionProcess\n          .getCollectionById(collectionId)\n          .resolveOption(s\"Can't find the collection with id $collectionId\")\n        _ <- di.trackEventProcess.editCollection(collection.name)\n        _ <- actions.initializeEditCollection(collection)\n      } yield ()\n\n    def createCollection() =\n      for {\n        _ <- di.trackEventProcess.createNewCollection()\n        _ <- actions.initializeNewCollection()\n      } yield ()\n\n    for {\n      theme <- getThemeTask\n      _     <- actions.initialize(theme)\n      _ <- maybeCollectionId match {\n        case Some(collectionId) => editCollection(collectionId.toInt)\n        case None               => createCollection()\n      }\n    } yield ()\n  }\n\n  def editCollection(\n      collection: Collection,\n      name: String,\n      icon: String,\n      themedColorIndex: Int): TaskService[Collection] = {\n    val request = CollectionData(\n      position = collection.position,\n      name = name,\n      collectionType = collection.collectionType,\n      icon = icon,\n      themedColorIndex = themedColorIndex,\n      appsCategory = collection.appsCategory,\n      cards = collection.cards map (_.toData),\n      moment = collection.moment map (_.toData))\n    for {\n      collection <- di.collectionProcess.editCollection(collection.id, request)\n      _          <- actions.close()\n    } yield collection\n  }\n\n  def saveCollection(name: String, icon: String, themedColorIndex: Int): TaskService[Collection] = {\n    val request = CollectionData(\n      name = name,\n      collectionType = FreeCollectionType,\n      icon = icon,\n      themedColorIndex = themedColorIndex,\n      appsCategory = None,\n      cards = Seq.empty,\n      moment = None)\n    for {\n      _          <- di.trackEventProcess.createNewCollection()\n      collection <- di.collectionProcess.addCollection(request)\n      _          <- actions.close()\n    } yield collection\n  }\n\n  def updateIcon(maybeIcon: Option[String]): TaskService[Unit] =\n    readOption(maybeIcon, \"Empty index color\")(actions.updateIcon)\n\n  def updateColor(maybeIndexColor: Option[Int]): TaskService[Unit] =\n    readOption(maybeIndexColor, \"Empty index color\")(actions.updateColor)\n\n  def changeColor(maybeColor: Option[Int]): TaskService[Unit] =\n    readOption(maybeColor, \"Empty color\")(actions.showColorDialog)\n\n  def changeIcon(maybeIcon: Option[String]): TaskService[Unit] =\n    readOption(maybeIcon, \"Empty Icon\")(actions.showIconDialog)\n\n  private[this] def readOption[T](maybe: Option[T], errorMessage: String)(\n      f: (T) => TaskService[Unit]): TaskService[Unit] =\n    maybe match {\n      case Some(value) => f(value)\n      case None        => TaskService.left(JobException(errorMessage))\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/createoreditcollection/CreateOrEditCollectionUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.createoreditcollection\n\nimport android.graphics.Paint.Style\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.OvalShape\nimport android.support.v4.app.DialogFragment\nimport android.widget.ImageView\nimport cards.nine.app.ui.commons.dialogs.{BaseActionFragment, Styles}\nimport cards.nine.app.ui.commons.ops.CollectionOps._\nimport cards.nine.app.ui.commons.ops.DrawableOps._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.{AppUtils, RequestCodes}\nimport cards.nine.app.ui.components.layouts.tweaks.DialogToolbarTweaks._\nimport cards.nine.commons._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.types.theme.{DrawerIconColor, DrawerTextColor}\nimport cards.nine.models.types.{Communication, DialogToolbarTitle}\nimport cards.nine.models.{Collection, NineCardsTheme}\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.EditTextTweaks._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\n\ntrait CreateOrEditCollectionUiActions extends Styles {\n\n  self: BaseActionFragment with CreateOrEditCollectionDOM with CreateOrEditCollectionListener =>\n\n  val tagDialog = \"create-or-edit-dialog\"\n\n  val tagLine = \"line\"\n\n  lazy val lineColor = theme.getLineColor\n\n  val defaultIcon = Communication.name\n\n  var statuses = CreateOrEditCollectionStatuses()\n\n  def initialize(theme: NineCardsTheme): TaskService[Unit] = {\n    statuses = statuses.copy(theme = theme)\n    val textColor = statuses.theme.get(DrawerTextColor)\n    ((toolbar <~\n      dtbNavigationOnClickListener((_) => unreveal())) ~\n      (rootView <~ colorLines()) ~\n      (name <~ tvColor(textColor) <~ tvHintColor(textColor.alpha(0.4f))) ~\n      (colorText <~ tvColor(textColor)) ~\n      (iconText <~ tvColor(textColor)) ~\n      (colorContent <~ On.click(Ui(changeColor(getColor)))) ~\n      (iconContent <~ On.click(Ui(changeIcon(getIcon))))).toService()\n  }\n\n  def initializeNewCollection(): TaskService[Unit] =\n    ((toolbar <~\n      dtbInit(colorPrimary) <~\n      dtbChangeText(R.string.newCollection)) ~\n      (fab <~\n        fabButtonMenuStyle(colorPrimary) <~\n        On.click(Ui(saveCollection(getName, getIcon, getColor)))) ~\n      setIcon(defaultIcon) ~\n      setIndexColor(0)).toService()\n\n  def initializeEditCollection(collection: Collection): TaskService[Unit] = {\n    val color = theme.getIndexColor(collection.themedColorIndex)\n    ((toolbar <~\n      dtbInit(color) <~\n      dtbChangeText(R.string.editCollection)) ~\n      (collectionName <~ tvText(collection.name)) ~\n      (fab <~\n        fabButtonMenuStyle(color) <~\n        On.click(Ui(editCollection(collection, getName, getIcon, getColor)))) ~\n      setIcon(collection.icon) ~\n      setIndexColor(collection.themedColorIndex)).toService()\n  }\n\n  def showColorDialog(color: Int): TaskService[Unit] = {\n    val dialog      = ColorDialogFragment(color)\n    val requestCode = RequestCodes.selectInfoColor\n    showDialog(dialog, requestCode).toService()\n  }\n\n  def showIconDialog(icon: String): TaskService[Unit] = {\n    val dialog      = IconDialogFragment(icon)\n    val requestCode = RequestCodes.selectInfoIcon\n    showDialog(dialog, requestCode).toService()\n  }\n\n  def showMessageContactUsError: TaskService[Unit] =\n    showMessage(R.string.contactUsError).toService()\n\n  def showMessageFormFieldError: TaskService[Unit] =\n    showMessage(R.string.formFieldError).toService()\n\n  def updateIcon(iconName: String): TaskService[Unit] =\n    setIcon(iconName).toService()\n\n  def updateColor(indexColor: Int): TaskService[Unit] =\n    setIndexColor(indexColor).toService()\n\n  def close(): TaskService[Unit] = (hideKeyboard ~ unreveal()).toService()\n\n  private[this] def colorLines() = Transformer {\n    case iv: ImageView if iv.getTag() == tagLine =>\n      iv <~ vBackgroundColor(lineColor)\n  }\n\n  private[this] def hideKeyboard: Ui[Any] = name <~ etHideKeyboard\n\n  private[this] def showDialog(dialog: DialogFragment, requestCode: Int) = Ui {\n    val ft = getFragmentManager.beginTransaction()\n    Option(getFragmentManager.findFragmentByTag(tagDialog)) foreach ft.remove\n    ft.addToBackStack(javaNull)\n    dialog.setTargetFragment(this, requestCode)\n    dialog.show(ft, tagDialog)\n  }\n\n  private[this] def setIcon(iconName: String): Ui[Any] =\n    iconImage <~\n      vTag(iconName) <~\n      ivSrc(resGetDrawable(iconName.getIconDetail).colorize(theme.get(DrawerIconColor)))\n\n  private[this] def setIndexColor(index: Int): Ui[Any] = {\n    val color    = theme.getIndexColor(index)\n    val size     = resGetDimensionPixelSize(R.dimen.size_icon_select_new_collection)\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.setIntrinsicHeight(size)\n    drawable.setIntrinsicWidth(size)\n    drawable.getPaint.setColor(color)\n    drawable.getPaint.setStyle(Style.FILL)\n    drawable.getPaint.setAntiAlias(true)\n    (toolbar <~\n      dtbInit(color)) ~\n      (fab <~\n        fabButtonMenuStyle(color)) ~\n      (colorImage <~\n        vTag(index) <~\n        ivSrc(drawable))\n  }\n\n  private[this] def showMessage(message: Int): Ui[Any] =\n    content <~ vSnackbarShort(message)\n\n  private[this] def getName: Option[String] = name.getText.toString match {\n    case s if s.nonEmpty => Some(s)\n    case _               => None\n  }\n\n  private[this] def getIcon: Option[String] =\n    Option(iconImage.getTag) flatMap {\n      case s: String => Some(s)\n      case _         => None\n    }\n\n  private[this] def getColor: Option[Int] =\n    Option(colorImage.getTag) map Int.unbox\n\n}\n\ncase class CreateOrEditCollectionStatuses(theme: NineCardsTheme = AppUtils.getDefaultTheme)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/createoreditcollection/IconDialogFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.createoreditcollection\n\nimport android.app.{Activity, Dialog}\nimport android.content.Intent\nimport android.os.Bundle\nimport android.support.v4.app.DialogFragment\nimport android.support.v7.app.AlertDialog\nimport android.view.LayoutInflater\nimport android.widget.{LinearLayout, ScrollView}\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.ops.CollectionOps._\nimport cards.nine.app.ui.commons.ops.DrawableOps._\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.models._\nimport cards.nine.models.types.ContactsCategory\nimport cards.nine.models.types.NineCardsCategory._\nimport cards.nine.models.types.NineCardsMoment._\nimport cards.nine.models.types.theme.{\n  DrawerBackgroundColor,\n  DrawerIconColor,\n  DrawerTextColor,\n  PrimaryColor\n}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\ncase class IconDialogFragment(\n    iconSelected: String)(implicit contextWrapper: ContextWrapper, theme: NineCardsTheme)\n    extends DialogFragment\n    with AppNineCardsIntentConversions {\n\n  val categoryIcons = appsCategories map { cat =>\n    val name =\n      resGetString(cat.getStringResource).getOrElse(cat.getStringResource)\n    ItemData(name, cat.getIconResource)\n  } sortBy (_.name)\n\n  val momentIcons = moments map { mom =>\n    val name =\n      resGetString(mom.getStringResource).getOrElse(mom.getStringResource)\n    ItemData(name, mom.getIconResource)\n  } sortBy (_.name)\n\n  val contactIcon = Seq {\n    val name = resGetString(ContactsCategory.getStringResource)\n      .getOrElse(ContactsCategory.getStringResource)\n    ItemData(name, ContactsCategory.getIconResource)\n  }\n\n  val icons = categoryIcons ++ momentIcons ++ contactIcon\n\n  override def onCreateDialog(savedInstanceState: Bundle): Dialog = {\n    val rootView    = new ScrollView(getActivity)\n    val contentView = new LinearLayout(getActivity)\n    contentView.setOrientation(LinearLayout.VERTICAL)\n\n    val views = icons map { ic =>\n      new ItemView(ic, ic.icon == iconSelected)\n    }\n\n    ((rootView <~ vBackgroundColor(theme.get(DrawerBackgroundColor)) <~ vgAddView(contentView)) ~\n      (contentView <~ vgAddViews(views))).run\n\n    new AlertDialog.Builder(getActivity).setView(rootView).create()\n  }\n\n  class ItemView(data: ItemData, select: Boolean)\n      extends LinearLayout(contextWrapper.bestAvailable)\n      with TypedFindView {\n\n    LayoutInflater.from(getActivity).inflate(R.layout.icon_info_item_dialog, this)\n\n    lazy val text = Option(findView(TR.icon_dialog_name))\n    lazy val icon = Option(findView(TR.icon_dialog_select))\n\n    val primaryColor = theme.get(PrimaryColor)\n\n    val colorizeDrawable =\n      resGetDrawable(data.icon.getIconDetail).colorize(theme.get(DrawerIconColor))\n\n    val drawable = PathMorphDrawable(\n      defaultIcon = IconTypes.CHECK,\n      defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_default),\n      defaultColor = primaryColor)\n\n    ((text <~\n      tvColor(if (select) primaryColor else theme.get(DrawerTextColor)) <~\n      tvText(data.name) <~\n      tvCompoundDrawablesWithIntrinsicBounds(left = Some(colorizeDrawable))) ~\n      (icon <~ (if (select) ivSrc(drawable) else Tweak.blank)) ~\n      (this <~ On.click {\n        Ui {\n          val responseIntent = new Intent\n          responseIntent.putExtra(CreateOrEditCollectionFragment.iconRequest, data.icon)\n          getTargetFragment.onActivityResult(\n            getTargetRequestCode,\n            Activity.RESULT_OK,\n            responseIntent)\n          dismiss()\n        }\n      })).run\n\n  }\n\n  case class ItemData(name: String, icon: String)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/editmoment/EditMomentDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.editmoment\n\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\n\ntrait EditMomentDOM { self: TypedFindView =>\n\n  lazy val iconLinkCollection = findView(TR.edit_moment_icon_link_collection)\n\n  lazy val nameLinkCollection = findView(TR.edit_moment_name_link_collection)\n\n  lazy val momentCollection = findView(TR.edit_moment_collection)\n\n  lazy val iconInfo = findView(TR.edit_moment_collection_info)\n\n  lazy val wifiRoot = findView(TR.edit_moment_wifi_root)\n\n  lazy val wifiContent = findView(TR.edit_moment_wifi_content)\n\n  lazy val iconWifi = findView(TR.edit_moment_icon_wifi)\n\n  lazy val nameWifi = findView(TR.edit_moment_name_wifi)\n\n  lazy val addWifiAction = findView(TR.edit_moment_add_wifi)\n\n  lazy val bluetoothRoot = findView(TR.edit_moment_bluetooth_root)\n\n  lazy val bluetoothContent = findView(TR.edit_moment_bluetooth_content)\n\n  lazy val iconBluetooth = findView(TR.edit_moment_icon_bluetooth)\n\n  lazy val nameBluetooth = findView(TR.edit_moment_name_bluetooth)\n\n  lazy val addBluetoothAction = findView(TR.edit_moment_add_bluetooth)\n\n  lazy val hourRoot = findView(TR.edit_moment_hours_root)\n\n  lazy val hourContent = findView(TR.edit_moment_hour_content)\n\n  lazy val addHourAction = findView(TR.edit_moment_add_hour)\n\n  lazy val iconHour = findView(TR.edit_moment_icon_hour)\n\n  lazy val nameHour = findView(TR.edit_moment_name_hour)\n\n  lazy val messageRoot = findView(TR.edit_moment_message_root)\n\n  lazy val messageIcon = findView(TR.edit_moment_icon_message)\n\n  lazy val messageName = findView(TR.edit_moment_name_message)\n\n  lazy val messageText = findView(TR.edit_moment_message)\n\n}\n\ntrait EditMomentListener {\n\n  def addWifi(): Unit\n\n  def addWifi(wifi: String): Unit\n\n  def addBluetooth(): Unit\n\n  def addBluetooth(device: String): Unit\n\n  def addHour(): Unit\n\n  def saveMoment(): Unit\n\n  def setCollectionId(collectionId: Option[Int]): Unit\n\n  def removeHour(position: Int): Unit\n\n  def removeWifi(position: Int): Unit\n\n  def removeBluetooth(position: Int): Unit\n\n  def changeFromHour(position: Int, hour: String): Unit\n\n  def changeToHour(position: Int, hour: String): Unit\n\n  def swapDay(position: Int, index: Int): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/editmoment/EditMomentFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.editmoment\n\nimport android.app.Dialog\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.commons.javaNull\nimport cards.nine.models.types.NineCardsMoment\nimport cards.nine.models.{Moment, MomentTimeSlot}\nimport com.fortysevendeg.ninecardslauncher.R\n\nclass EditMomentFragment\n    extends BaseActionFragment\n    with EditMomentUiActions\n    with EditMomentDOM\n    with EditMomentListener\n    with AppNineCardsIntentConversions { self =>\n\n  lazy val momentType = Option(\n    getString(Seq(getArguments), EditMomentFragment.momentKey, javaNull))\n\n  lazy val editJobs = new EditMomentJobs(self)\n\n  override def useFab: Boolean = true\n\n  override def getLayoutId: Int = R.layout.edit_moment\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n    momentType match {\n      case Some(moment) =>\n        editJobs.initialize(NineCardsMoment(moment)).resolveServiceOr(_ => close())\n      case _ => editJobs.momentNotFound().resolveAsync()\n    }\n  }\n\n  override def addWifi(): Unit =\n    editJobs.addWifi().resolveAsyncServiceOr(_ => showFieldErrorMessage())\n\n  override def addWifi(wifi: String): Unit =\n    editJobs.addWifi(wifi).resolveAsync()\n\n  override def addBluetooth(): Unit =\n    editJobs.addBluetooth().resolveAsyncServiceOr(_ => showFieldErrorMessage())\n\n  override def addBluetooth(device: String): Unit =\n    editJobs.addBluetooth(device).resolveAsync()\n\n  override def addHour(): Unit = editJobs.addHour().resolveAsync()\n\n  override def saveMoment(): Unit =\n    editJobs.saveMoment().resolveAsyncServiceOr(_ => showSavingMomentErrorMessage())\n\n  override def setCollectionId(collectionId: Option[Int]): Unit =\n    editJobs.setCollectionId(collectionId).resolveAsync()\n\n  override def removeHour(position: Int): Unit =\n    editJobs.removeHour(position).resolveAsync()\n\n  override def removeWifi(position: Int): Unit =\n    editJobs.removeWifi(position).resolveAsync()\n\n  override def removeBluetooth(position: Int): Unit =\n    editJobs.removeBluetooth(position).resolveAsync()\n\n  override def changeFromHour(position: Int, hour: String): Unit =\n    editJobs\n      .changeFromHour(position, hour)\n      .resolveAsyncServiceOr(_ => showSavingMomentErrorMessage())\n\n  override def changeToHour(position: Int, hour: String): Unit =\n    editJobs\n      .changeToHour(position, hour)\n      .resolveAsyncServiceOr(_ => showSavingMomentErrorMessage())\n\n  override def swapDay(position: Int, index: Int): Unit =\n    editJobs.swapDay(position, index).resolveAsyncServiceOr(_ => showSavingMomentErrorMessage())\n}\n\nobject EditMomentFragment {\n\n  val momentKey = \"moment\"\n\n  var statuses = EditMomentStatuses()\n\n}\n\ncase class EditMomentStatuses(moment: Option[Moment] = None, modifiedMoment: Option[Moment] = None) {\n\n  def start(m: Moment): EditMomentStatuses =\n    copy(moment = Option(m), modifiedMoment = Option(m))\n\n  def setCollectionId(collectionId: Option[Int]): EditMomentStatuses =\n    copy(modifiedMoment = modifiedMoment map (_.copy(collectionId = collectionId)))\n\n  def swapDay(position: Int, day: Int): EditMomentStatuses = {\n    val modMoment = modifiedMoment map { m =>\n      val timeslots = m.timeslot.zipWithIndex map {\n        case (timeslot, index) if index == position =>\n          val modDays = timeslot.days.zipWithIndex map {\n            case (s, indexDay) if indexDay == day && s == 0 => 1\n            case (s, indexDay) if indexDay == day           => 0\n            case (s, indexDay)                              => s\n          }\n          timeslot.copy(days = modDays)\n        case (timeslot, _) => timeslot\n      }\n      m.copy(timeslot = timeslots)\n    }\n    copy(modifiedMoment = modMoment)\n  }\n\n  def changeFromHour(position: Int, hour: String): EditMomentStatuses = {\n    val modMoment = modifiedMoment map { m =>\n      val timeslots = m.timeslot.zipWithIndex map {\n        case (timeslot, index) if index == position =>\n          timeslot.copy(from = hour)\n        case (timeslot, _) => timeslot\n      }\n      m.copy(timeslot = timeslots)\n    }\n    copy(modifiedMoment = modMoment)\n  }\n\n  def changeToHour(position: Int, hour: String): EditMomentStatuses = {\n    val modMoment = modifiedMoment map { m =>\n      val timeslots = m.timeslot.zipWithIndex map {\n        case (timeslot, index) if index == position => timeslot.copy(to = hour)\n        case (timeslot, _)                          => timeslot\n      }\n      m.copy(timeslot = timeslots)\n    }\n    copy(modifiedMoment = modMoment)\n  }\n\n  def addHour(time: MomentTimeSlot): EditMomentStatuses = {\n    val modMoment = modifiedMoment map { m =>\n      m.copy(timeslot = m.timeslot :+ time)\n    }\n    copy(modifiedMoment = modMoment)\n  }\n\n  def removeHour(position: Int): EditMomentStatuses = {\n    val modMoment = modifiedMoment map { m =>\n      m.copy(timeslot = m.timeslot.filterNot(m.timeslot.indexOf(_) == position))\n    }\n    copy(modifiedMoment = modMoment)\n  }\n\n  def addWifi(wifi: String): EditMomentStatuses = {\n    val modMoment = modifiedMoment map { m =>\n      m.copy(wifi = m.wifi :+ wifi)\n    }\n    copy(modifiedMoment = modMoment)\n  }\n\n  def removeWifi(position: Int): EditMomentStatuses = {\n    val modMoment = modifiedMoment map { m =>\n      m.copy(wifi = m.wifi.filterNot(m.wifi.indexOf(_) == position))\n    }\n    copy(modifiedMoment = modMoment)\n  }\n\n  def addBluetooth(device: String): EditMomentStatuses = {\n    val modMoment = modifiedMoment map { m =>\n      m.copy(bluetooth = m.bluetooth :+ device)\n    }\n    copy(modifiedMoment = modMoment)\n  }\n\n  def removeBluetooth(position: Int): EditMomentStatuses = {\n    val modMoment = modifiedMoment map { m =>\n      m.copy(bluetooth = m.bluetooth.filterNot(m.bluetooth.indexOf(_) == position))\n    }\n    copy(modifiedMoment = modMoment)\n  }\n\n  def wasModified(): Boolean = moment != modifiedMoment\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/editmoment/EditMomentJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.editmoment\n\nimport cards.nine.app.ui.commons.action_filters.MomentConstrainsChangedActionFilter\nimport cards.nine.app.ui.commons.{BroadAction, JobException, Jobs}\nimport cards.nine.app.ui.commons.dialogs.editmoment.EditMomentFragment._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.{TaskService, _}\nimport cards.nine.models.types.NineCardsMoment\nimport cards.nine.models.{Moment, MomentTimeSlot}\nimport macroid.ActivityContextWrapper\n\nclass EditMomentJobs(actions: EditMomentUiActions)(implicit contextWrapper: ActivityContextWrapper)\n    extends Jobs {\n\n  def initialize(nineCardsMoment: NineCardsMoment): TaskService[Unit] =\n    for {\n      _           <- di.trackEventProcess.editMoment(nineCardsMoment.name)\n      moment      <- di.momentProcess.getMomentByType(nineCardsMoment)\n      collections <- di.collectionProcess.getCollections\n      _           <- updateStatus(statuses.start(moment))\n      _           <- actions.initialize(moment, collections)\n    } yield ()\n\n  def momentNotFound(): TaskService[Unit] = actions.close()\n\n  def setCollectionId(collectionId: Option[Int]): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.quickAccessToCollection()\n      _ <- updateStatus(statuses.setCollectionId(collectionId match {\n        case Some(0) => None\n        case id      => id\n      }))\n    } yield ()\n\n  def swapDay(position: Int, day: Int): TaskService[Unit] = {\n    statuses = statuses.swapDay(position, day)\n    changePosition(position)\n  }\n\n  def changeFromHour(position: Int, hour: String): TaskService[Unit] = {\n    statuses = statuses.changeFromHour(position, hour)\n    changePosition(position)\n  }\n\n  def changeToHour(position: Int, hour: String): TaskService[Unit] = {\n    statuses = statuses.changeToHour(position, hour)\n    changePosition(position)\n  }\n\n  def addHour(): TaskService[Unit] = {\n    val newTimeslot      = MomentTimeSlot(from = \"9:00\", to = \"14:00\", days = Seq(0, 0, 0, 0, 0, 0, 0))\n    val containsTimeslot = statuses.modifiedMoment exists (_.timeslot.contains(newTimeslot))\n    if (containsTimeslot) {\n      actions.showItemDuplicatedMessage()\n    } else {\n      for {\n        _ <- di.trackEventProcess.setHours()\n        _ <- updateStatus(statuses.addHour(newTimeslot))\n        _ <- statuses.modifiedMoment match {\n          case Some(moment) => actions.loadHours(moment)\n          case _            => actions.showSavingMomentErrorMessage()\n        }\n      } yield ()\n    }\n  }\n\n  def removeHour(position: Int): TaskService[Unit] =\n    for {\n      _ <- updateStatus(statuses.removeHour(position))\n      _ <- statuses.modifiedMoment match {\n        case Some(moment) => actions.loadHours(moment)\n        case _            => actions.showSavingMomentErrorMessage()\n      }\n    } yield ()\n\n  def addWifi(): TaskService[Unit] =\n    for {\n      wifis <- di.deviceProcess.getConfiguredNetworks\n      _     <- actions.showWifiDialog(wifis)\n    } yield ()\n\n  def addWifi(wifi: String): TaskService[Unit] = {\n    val containsWifi = statuses.modifiedMoment exists (_.wifi.contains(wifi))\n    if (containsWifi) {\n      actions.showItemDuplicatedMessage()\n    } else {\n      for {\n        _ <- di.trackEventProcess.setWifi()\n        _ <- updateStatus(statuses.addWifi(wifi))\n        _ <- statuses.modifiedMoment match {\n          case Some(moment) => actions.loadWifis(moment)\n          case _            => actions.showSavingMomentErrorMessage()\n        }\n      } yield ()\n    }\n  }\n\n  def removeWifi(position: Int): TaskService[Unit] =\n    for {\n      _ <- updateStatus(statuses.removeWifi(position))\n      _ <- statuses.modifiedMoment match {\n        case Some(moment) => actions.loadWifis(moment)\n        case _            => actions.showSavingMomentErrorMessage()\n      }\n    } yield ()\n\n  def addBluetooth(): TaskService[Unit] =\n    for {\n      devices <- di.deviceProcess.getPairedBluetoothDevices\n      _       <- actions.showBluetoothDialog(devices)\n    } yield ()\n\n  def addBluetooth(device: String): TaskService[Unit] = {\n    val containsBluetooth = statuses.modifiedMoment exists (_.bluetooth.contains(device))\n    if (containsBluetooth) {\n      actions.showItemDuplicatedMessage()\n    } else {\n      for {\n        _ <- di.trackEventProcess.setBluetooth()\n        _ <- updateStatus(statuses.addBluetooth(device))\n        _ <- statuses.modifiedMoment match {\n          case Some(moment) => actions.loadBluetooth(moment)\n          case _            => actions.showSavingMomentErrorMessage()\n        }\n      } yield ()\n    }\n  }\n\n  def removeBluetooth(position: Int): TaskService[Unit] =\n    for {\n      _ <- updateStatus(statuses.removeBluetooth(position))\n      _ <- statuses.modifiedMoment match {\n        case Some(moment) => actions.loadBluetooth(moment)\n        case _            => actions.showSavingMomentErrorMessage()\n      }\n    } yield ()\n\n  def saveMoment(): TaskService[Unit] =\n    (statuses.wasModified(), statuses.modifiedMoment) match {\n      case (true, Some(moment)) =>\n        val request = Moment(\n          id = moment.id,\n          collectionId = moment.collectionId,\n          timeslot = moment.timeslot,\n          wifi = moment.wifi,\n          bluetooth = moment.bluetooth,\n          headphone = moment.headphone,\n          momentType = moment.momentType)\n        for {\n          _ <- di.momentProcess.updateMoment(request)\n          _ <- sendBroadCastTask(BroadAction(MomentConstrainsChangedActionFilter.action))\n          _ <- actions.close()\n        } yield ()\n      case _ => actions.close()\n    }\n\n  private[this] def changePosition(position: Int): TaskService[Unit] = {\n    val timeslot = statuses.modifiedMoment flatMap (_.timeslot.lift(position))\n    timeslot match {\n      case Some(ts) => actions.reloadDays(position, ts)\n      case _        => TaskService.left(JobException(\"Timeslot not found\"))\n    }\n  }\n\n  private[this] def updateStatus(editMomentStatuses: EditMomentStatuses): TaskService[Unit] =\n    TaskService.right(statuses = editMomentStatuses)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/editmoment/EditMomentUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.editmoment\n\nimport android.support.v4.app.DialogFragment\nimport android.view.Gravity\nimport android.widget.{ImageView, TextView}\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.dialogs.{BaseActionFragment, Styles}\nimport cards.nine.app.ui.commons.ops.CollectionOps._\nimport cards.nine.app.ui.commons.ops.DrawableOps._\nimport cards.nine.app.ui.commons.ops.NineCardsMomentOps._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.dialogs.{\n  AlertDialogFragment,\n  BluetoothDialogFragment,\n  WifiDialogFragment\n}\nimport cards.nine.app.ui.components.layouts.tweaks.DialogToolbarTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.EditHourMomentLayoutTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.EditDeviceMomentLayoutTweaks._\nimport cards.nine.app.ui.components.layouts.{EditDeviceMomentLayout, EditHourMomentLayout}\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.commons._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.commons.services.TaskService.{TaskService, _}\nimport cards.nine.models.types.theme.{DrawerIconColor, DrawerTextColor}\nimport cards.nine.models.types.{CarMoment, MusicMoment, OutAndAboutMoment}\nimport cards.nine.models.{Collection, Moment, MomentTimeSlot}\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.UIActionsExtras._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\n\ntrait EditMomentUiActions extends Styles {\n\n  self: BaseActionFragment with EditMomentDOM with EditMomentListener =>\n\n  val defaultIcon = R.drawable.icon_collection_default_detail\n\n  val tagWifiDialog = \"wifi-dialog\"\n\n  val tagBluetoothDialog = \"bluetooth-dialog\"\n\n  val tagDefaultDialog = \"default-dialog\"\n\n  val tagLine = \"line\"\n\n  lazy val lineColor = theme.getLineColor\n\n  def initialize(moment: Moment, collections: Seq[Collection]): TaskService[Unit] = {\n    val iconColor = theme.get(DrawerIconColor)\n    val textColor = theme.get(DrawerTextColor)\n    val arrow =\n      resGetDrawable(R.drawable.icon_edit_moment_arrow).colorize(iconColor)\n\n    def showMessageContent =\n      (hourRoot <~ vGone) ~\n        (messageIcon <~ tivDefaultColor(iconColor) <~ ivSrc(\n          moment.momentType.getIconCollectionDetail)) ~\n        (messageName <~ tvText(\n          resGetString(R.string.message_moment_name, moment.momentType.getName)))\n\n    def loadInfoByMoment = moment.momentType match {\n      case CarMoment =>\n        showMessageContent ~\n          (messageText <~ tvText(R.string.specially_conditions_car))\n      case MusicMoment =>\n        showMessageContent ~\n          (messageText <~ tvText(R.string.specially_conditions_music))\n      case OutAndAboutMoment =>\n        showMessageContent ~\n          (Seq(wifiRoot, bluetoothRoot) <~ vGone) ~\n          (messageText <~ tvText(R.string.specially_conditions_out_and_about))\n      case _ =>\n        messageRoot <~ vGone\n    }\n\n    val init = ((toolbar <~\n      dtbInit(colorPrimary) <~\n      dtbChangeText(resGetString(R.string.editMomentWithName, moment.momentType.getName)) <~\n      dtbNavigationOnClickListener((_) => unreveal())) ~\n      (rootView <~ colorLines()) ~\n      (iconInfo <~ tivDefaultColor(iconColor) <~ On.click(showLinkCollectionMessage())) ~\n      (Seq(iconLinkCollection, iconWifi, iconBluetooth, iconHour) <~ tivDefaultColor(iconColor)) ~\n      (addWifiAction <~ tivDefaultColor(iconColor) <~ On.click(Ui(addWifi()))) ~\n      (addBluetoothAction <~ tivDefaultColor(iconColor) <~ On.click(Ui(addBluetooth()))) ~\n      (addHourAction <~ tivDefaultColor(iconColor) <~ On.click(Ui(addHour()))) ~\n      (Seq(nameWifi, nameHour, messageName, messageText, nameLinkCollection) <~ tvColor(textColor)) ~\n      (momentCollection <~\n        tvColor(textColor) <~\n        tvSizeResource(R.dimen.text_large) <~\n        tvCompoundDrawablesWithIntrinsicBounds(right = Option(arrow))) ~\n      (fab <~\n        fabButtonMenuStyle(colorPrimary) <~\n        On.click(Ui(saveMoment()))) ~\n      loadCategories(moment, collections) ~\n      loadInfoByMoment).toService()\n    for {\n      _ <- init\n      _ <- loadHours(moment)\n      _ <- loadWifis(moment)\n      _ <- loadBluetooth(moment)\n    } yield ()\n  }\n\n  def close(): TaskService[Unit] = unreveal().toService()\n\n  def showSavingMomentErrorMessage(): TaskService[Unit] =\n    uiShortToast(R.string.contactUsError).toService()\n\n  def reloadDays(position: Int, timeslot: MomentTimeSlot): TaskService[Unit] =\n    (hourContent <~ Transformer {\n      case view: EditHourMomentLayout if view.getPosition.contains(position) =>\n        view <~ ehmPopulate(timeslot, position, removeHour, changeFromHour, changeToHour, swapDay)\n    }).toService()\n\n  def loadHours(moment: Moment): TaskService[Unit] = {\n    val views = if (moment.timeslot.nonEmpty) {\n      moment.timeslot.zipWithIndex map {\n        case (slot, index) =>\n          (w[EditHourMomentLayout] <~ ehmPopulate(\n            slot,\n            index,\n            removeHour,\n            changeFromHour,\n            changeToHour,\n            swapDay)).get\n      }\n    } else {\n      Seq(createMessage(R.string.addHoursToEditMoment))\n    }\n    (hourContent <~ vgRemoveAllViews <~ vgAddViews(views)).toService()\n  }\n\n  def showWifiDialog(wifis: Seq[String]): TaskService[Unit] = {\n    val dialog = WifiDialogFragment(wifis, addWifi)\n    showDialog(dialog, tagWifiDialog).toService()\n  }\n\n  def showBluetoothDialog(devices: Seq[String]): TaskService[Unit] = {\n    val dialog = BluetoothDialogFragment(devices, addBluetooth)\n    showDialog(dialog, tagBluetoothDialog).toService()\n  }\n\n  def loadWifis(moment: Moment): TaskService[Unit] = {\n    val views = if (moment.wifi.nonEmpty) {\n      moment.wifi.zipWithIndex map {\n        case (wifi, index) =>\n          (w[EditDeviceMomentLayout] <~ edmPopulate(wifi, index, removeWifi)).get\n      }\n    } else {\n      Seq(createMessage(R.string.addWifiToEditMoment))\n    }\n    (wifiContent <~ vgRemoveAllViews <~ vgAddViews(views)).toService()\n  }\n\n  def loadBluetooth(moment: Moment): TaskService[Unit] = {\n    val views = if (moment.bluetooth.nonEmpty) {\n      moment.bluetooth.zipWithIndex map {\n        case (bluetooth, index) =>\n          (w[EditDeviceMomentLayout] <~ edmPopulate(bluetooth, index, removeBluetooth)).get\n      }\n    } else {\n      Seq(createMessage(R.string.addBluetoothToEditMoment))\n    }\n    (bluetoothContent <~ vgRemoveAllViews <~ vgAddViews(views)).toService()\n  }\n\n  def showFieldErrorMessage(): TaskService[Unit] =\n    uiShortToast(R.string.contactUsError).toService()\n\n  def showItemDuplicatedMessage(): TaskService[Unit] =\n    uiShortToast(R.string.addDuplicateItemError).toService()\n\n  private[this] def colorLines() = Transformer {\n    case iv: ImageView if iv.getTag() == tagLine =>\n      iv <~ vBackgroundColor(lineColor)\n  }\n\n  private[this] def showLinkCollectionMessage() = Ui {\n    val dialog = new AlertDialogFragment(\n      message = R.string.linkCollectionMessage,\n      showCancelButton = false\n    )\n    dialog.show(getFragmentManager, tagDefaultDialog)\n  }\n\n  private[this] def loadCategories(moment: Moment, collections: Seq[Collection]): Ui[Any] = {\n    val collectionIds   = 0 +: (collections map (_.id))\n    val collectionNames = resGetString(R.string.noLinkCollectionToMoment) +: (collections map (_.name))\n    val icons           = defaultIcon +: (collections map (_.getIconDetail))\n\n    def setName(position: Int) =\n      momentCollection <~ tvText(collectionNames.lift(position) getOrElse \"NO\")\n\n    val spinnerPosition = moment.collectionId map collectionIds.indexOf getOrElse 0\n\n    (momentCollection <~\n      On.click {\n        momentCollection <~\n          vListThemedPopupWindowShow(\n            icons = icons,\n            values = collectionNames,\n            onItemClickListener = (position) => {\n              setName(position).run\n              setCollectionId(collectionIds.lift(position))\n            },\n            height = Option(resGetDimensionPixelSize(R.dimen.height_list_popup_menu))\n          )\n      }) ~\n      setName(spinnerPosition)\n  }\n\n  private[this] def showDialog(dialog: DialogFragment, tag: String) = Ui {\n    val ft = getFragmentManager.beginTransaction()\n    Option(getFragmentManager.findFragmentByTag(tag)) foreach ft.remove\n    ft.addToBackStack(javaNull)\n    dialog.show(ft, tag)\n  }\n\n  private[this] def createMessage(res: Int) = {\n    val textColor = theme.get(DrawerTextColor).alpha(.4f)\n    val padding   = resGetDimensionPixelSize(R.dimen.padding_large)\n    (w[TextView] <~\n      vMatchWidth <~\n      vPaddings(padding) <~\n      tvGravity(Gravity.CENTER_HORIZONTAL) <~\n      tvText(res) <~\n      tvSizeResource(R.dimen.text_default) <~\n      tvColor(textColor)).get\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/privatecollections/PrivateCollectionsAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.privatecollections\n\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.OvalShape\nimport android.support.v7.widget.{LinearLayoutManager, RecyclerView}\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.ops.CollectionOps._\nimport cards.nine.app.ui.commons.styles.{CollectionCardsStyles, CommonStyles}\nimport cards.nine.models.{CardData, CollectionData, NineCardsTheme}\nimport macroid.extras.ImageViewTweaks._\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\ncase class PrivateCollectionsAdapter(\n    privateCollections: Seq[CollectionData],\n    onClick: (CollectionData => Unit))(\n    implicit activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[ViewHolderPrivateCollectionsLayoutAdapter] {\n\n  override def onCreateViewHolder(\n      parent: ViewGroup,\n      viewType: Int): ViewHolderPrivateCollectionsLayoutAdapter = {\n    val view = LayoutInflater\n      .from(parent.getContext)\n      .inflate(TR.layout.private_collections_item, parent, false)\n    ViewHolderPrivateCollectionsLayoutAdapter(view)\n  }\n\n  override def getItemCount: Int = privateCollections.size\n\n  override def onBindViewHolder(\n      viewHolder: ViewHolderPrivateCollectionsLayoutAdapter,\n      position: Int): Unit = {\n    val privateCollection = privateCollections(position)\n    viewHolder.bind(privateCollection, onClick).run\n  }\n\n  def getLayoutManager = new LinearLayoutManager(activityContext.application)\n\n}\n\ncase class ViewHolderPrivateCollectionsLayoutAdapter(content: ViewGroup)(\n    implicit context: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView\n    with CollectionCardsStyles\n    with CommonStyles {\n\n  val appsByRow = 5\n\n  lazy val root = findView(TR.private_collections_item_layout)\n\n  lazy val iconContent = findView(TR.private_collections_item_content)\n\n  lazy val icon = findView(TR.private_collections_item_icon)\n\n  lazy val name = findView(TR.private_collections_item_name)\n\n  lazy val appsRow = findView(TR.private_collections_item_row)\n\n  lazy val addCollection = findView(TR.private_collections_item_add_collection)\n\n  lazy val line = findView(TR.private_collections_item_line)\n\n  ((root <~ cardRootStyle) ~\n    (name <~ titleTextStyle) ~\n    (line <~ vBackgroundColor(theme.getLineColor)) ~\n    (addCollection <~ buttonStyle)).run\n\n  def bind(collection: CollectionData, onClick: (CollectionData => Unit)): Ui[_] = {\n    val background = new ShapeDrawable(new OvalShape)\n    background.getPaint.setColor(theme.getIndexColor(collection.themedColorIndex))\n    (iconContent <~ vBackground(background)) ~\n      (icon <~ ivSrc(collection.getIconCollectionDetail)) ~\n      (appsRow <~\n        vgRemoveAllViews <~\n        fblAddItems(collection.cards, (item: CardData) => {\n          ivSrcByPackageName(item.packageName, item.term)\n        })) ~\n      (name <~ tvText(collection.name)) ~\n      (addCollection <~ On.click(Ui(onClick(collection))))\n  }\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/privatecollections/PrivateCollectionsDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.privatecollections\n\nimport cards.nine.models.{Collection, CollectionData}\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\n\ntrait PrivateCollectionsDOM { self: TypedFindView =>\n\n  lazy val recycler = findView(TR.actions_recycler)\n\n}\n\ntrait PrivateCollectionsListener {\n\n  def loadPrivateCollections(): Unit\n\n  def saveCollection(collection: CollectionData): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/privatecollections/PrivateCollectionsFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.privatecollections\n\nimport android.app.Dialog\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.launcher.jobs.LauncherJobs\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.CollectionData\nimport cards.nine.models.types.theme.CardLayoutBackgroundColor\nimport com.fortysevendeg.ninecardslauncher.R\n\nclass PrivateCollectionsFragment(implicit launcherJobs: LauncherJobs)\n    extends BaseActionFragment\n    with PrivateCollectionsDOM\n    with PrivateCollectionsUiActions\n    with PrivateCollectionsListener\n    with AppNineCardsIntentConversions { self =>\n\n  lazy val collectionJobs = new PrivateCollectionsJobs(self)\n\n  lazy val packages =\n    getSeqString(Seq(getArguments), BaseActionFragment.packages, Seq.empty[String])\n\n  override def getLayoutId: Int = R.layout.list_action_fragment\n\n  override protected lazy val backgroundColor: Int =\n    theme.get(CardLayoutBackgroundColor)\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n    collectionJobs.initialize().resolveAsync()\n  }\n\n  override def loadPrivateCollections(): Unit =\n    collectionJobs\n      .loadPrivateCollections()\n      .resolveServiceOr(_ => showErrorLoadingCollectionInScreen())\n\n  override def saveCollection(collection: CollectionData): Unit = {\n    (for {\n      collectionAdded <- collectionJobs.saveCollection(collection)\n      _               <- launcherJobs.addCollection(collectionAdded)\n    } yield ()).resolveServiceOr(_ => showErrorSavingCollectionInScreen())\n  }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/privatecollections/PrivateCollectionsJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.privatecollections\n\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{Collection, CollectionData}\nimport cards.nine.models.types.GetByName\nimport macroid.ActivityContextWrapper\n\nclass PrivateCollectionsJobs(actions: PrivateCollectionsUiActions)(\n    implicit contextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with Conversions {\n\n  def initialize(): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.openMyCollections()\n      _ <- actions.initialize()\n      _ <- loadPrivateCollections()\n    } yield ()\n\n  def loadPrivateCollections(): TaskService[Unit] =\n    for {\n      _              <- actions.showLoading()\n      collections    <- di.collectionProcess.getCollections\n      moments        <- di.momentProcess.getMoments\n      apps           <- di.deviceProcess.getSavedApps(GetByName)\n      newCollections <- di.collectionProcess.generatePrivateCollections(apps)\n      privateCollections = newCollections filterNot { newCollection =>\n        newCollection.appsCategory match {\n          case Some(category) =>\n            (collections flatMap (_.appsCategory)) contains category\n          case _ => false\n        }\n      }\n      _ <- if (privateCollections.isEmpty) {\n        actions.showEmptyMessageInScreen()\n      } else {\n        actions.addPrivateCollections(privateCollections)\n      }\n    } yield ()\n\n  def saveCollection(collection: CollectionData): TaskService[Collection] =\n    for {\n      _               <- di.trackEventProcess.createNewCollectionFromMyCollection(collection.name)\n      collectionAdded <- di.collectionProcess.addCollection(collection)\n      _               <- actions.close()\n    } yield collectionAdded\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/privatecollections/PrivateCollectionsUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.privatecollections\n\nimport cards.nine.app.ui.commons.dialogs.{BaseActionFragment, Styles}\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.commons.PaddingItemDecoration\nimport cards.nine.app.ui.components.layouts.tweaks.DialogToolbarTweaks._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.CollectionData\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.ViewTweaks._\n\ntrait PrivateCollectionsUiActions extends Styles {\n\n  self: BaseActionFragment with PrivateCollectionsDOM with PrivateCollectionsListener =>\n\n  def initialize(): TaskService[Unit] =\n    ((toolbar <~\n      dtbInit(colorPrimary) <~\n      dtbChangeText(R.string.myCollections) <~\n      dtbNavigationOnClickListener((_) => unreveal())) ~\n      (recycler <~ recyclerStyle)).toService()\n\n  def addPrivateCollections(privateCollections: Seq[CollectionData]): TaskService[Unit] = {\n    val adapter = PrivateCollectionsAdapter(privateCollections, saveCollection)\n    ((recycler <~\n      vVisible <~\n      rvAddItemDecoration(new PaddingItemDecoration) <~\n      rvLayoutManager(adapter.getLayoutManager) <~\n      rvAdapter(adapter)) ~\n      (loading <~ vGone)).toService()\n  }\n\n  def showLoading(): TaskService[Unit] =\n    ((loading <~ vVisible) ~ (recycler <~ vGone)).toService()\n\n  def showEmptyMessageInScreen(): TaskService[Unit] =\n    showMessageInScreen(R.string.emptyPrivateCollections, error = false, loadPrivateCollections())\n      .toService()\n\n  def showErrorLoadingCollectionInScreen(): TaskService[Unit] =\n    showMessageInScreen(\n      R.string.errorLoadingPrivateCollections,\n      error = true,\n      loadPrivateCollections()).toService()\n\n  def showErrorSavingCollectionInScreen(): TaskService[Unit] =\n    showMessageInScreen(\n      R.string.errorSavingPrivateCollections,\n      error = true,\n      loadPrivateCollections()).toService()\n\n  def close(): TaskService[Unit] = unreveal().toService()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/publicollections/PublicCollectionsDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.publicollections\n\nimport android.support.v4.app.Fragment\nimport android.view.Gravity\nimport android.widget.{LinearLayout, TextView}\nimport cards.nine.models.{Collection, SharedCollection}\nimport cards.nine.models.types.{NineCardsCategory, TypeSharedCollection}\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.Contexts\nimport macroid.FullDsl._\n\ntrait PublicCollectionsDOM { self: TypedFindView with Contexts[Fragment] =>\n\n  lazy val recycler = findView(TR.actions_recycler)\n\n  var typeFilter = slot[TextView]\n\n  var categoryFilter = slot[TextView]\n\n  lazy val tabsMenu = l[LinearLayout](\n    w[TextView] <~\n      wire(typeFilter) <~\n      tabButtonStyle(R.string.top),\n    w[TextView] <~\n      wire(categoryFilter) <~\n      tabButtonStyle(R.string.communication)).get\n\n  def tabButtonStyle(text: Int) = {\n    val paddingDefault = resGetDimensionPixelSize(R.dimen.padding_default)\n    val paddingLarge   = resGetDimensionPixelSize(R.dimen.padding_large)\n    val paddingSmall   = resGetDimensionPixelSize(R.dimen.padding_small)\n    vWrapContent +\n      tvText(text) +\n      tvNormalMedium +\n      tvSizeResource(R.dimen.text_large) +\n      tvGravity(Gravity.CENTER_VERTICAL) +\n      tvColorResource(R.color.tab_public_collection_dialog) +\n      vPadding(\n        paddingTop = paddingDefault,\n        paddingBottom = paddingDefault,\n        paddingRight = paddingLarge) +\n      tvDrawablePadding(paddingSmall) +\n      tvCompoundDrawablesWithIntrinsicBoundsResources(right = R.drawable.tab_menu_indicator)\n  }\n\n}\n\ntrait PublicCollectionsListener {\n\n  def loadPublicCollectionsByTypeSharedCollection(typeSharedCollection: TypeSharedCollection): Unit\n\n  def loadPublicCollectionsByCategory(category: NineCardsCategory): Unit\n\n  def loadPublicCollections(): Unit\n\n  def onAddCollection(sharedCollection: SharedCollection): Unit\n\n  def onShareCollection(sharedCollection: SharedCollection): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/publicollections/PublicCollectionsFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.publicollections\n\nimport android.app.Dialog\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.AppLog\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.launcher.jobs.LauncherJobs\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.SharedCollection\nimport cards.nine.models.types.theme.CardLayoutBackgroundColor\nimport cards.nine.models.types.{\n  Communication,\n  NineCardsCategory,\n  TopSharedCollection,\n  TypeSharedCollection\n}\nimport cards.nine.process.sharedcollections.SharedCollectionsConfigurationException\nimport com.fortysevendeg.ninecardslauncher.R\nimport monix.execution.cancelables.SerialCancelable\nimport PublicCollectionsFragment._\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\n\nclass PublicCollectionsFragment(implicit launcherJobs: LauncherJobs)\n    extends BaseActionFragment\n    with PublicCollectionsUiActions\n    with PublicCollectionsDOM\n    with PublicCollectionsListener\n    with AppNineCardsIntentConversions { self =>\n\n  lazy val collectionJobs = new PublicCollectionsJobs(self)\n\n  lazy val packages =\n    getSeqString(Seq(getArguments), BaseActionFragment.packages, Seq.empty[String])\n\n  override def getLayoutId: Int = R.layout.list_action_fragment\n\n  override protected lazy val backgroundColor: Int =\n    theme.get(CardLayoutBackgroundColor)\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n    statuses = statuses.reset\n    collectionJobs.initialize().resolveServiceOr(onError)\n  }\n\n  override def loadPublicCollectionsByTypeSharedCollection(\n      typeSharedCollection: TypeSharedCollection): Unit =\n    serialCancelableTaskRef := collectionJobs\n      .loadPublicCollectionsByTypeSharedCollection(typeSharedCollection)\n      .resolveAutoCancelableAsyncServiceOr(onError)\n\n  override def loadPublicCollectionsByCategory(category: NineCardsCategory): Unit =\n    serialCancelableTaskRef := collectionJobs\n      .loadPublicCollectionsByCategory(category)\n      .resolveAutoCancelableAsyncServiceOr(onError)\n\n  override def loadPublicCollections(): Unit =\n    serialCancelableTaskRef := collectionJobs\n      .loadPublicCollections()\n      .resolveAutoCancelableAsyncServiceOr(onError)\n\n  override def onAddCollection(sharedCollection: SharedCollection): Unit =\n    (for {\n      collection <- collectionJobs.saveSharedCollection(sharedCollection)\n      _          <- launcherJobs.addCollection(collection)\n    } yield ()).resolveServiceOr(_ => showErrorSavingCollectionInScreen())\n\n  override def onShareCollection(sharedCollection: SharedCollection): Unit =\n    collectionJobs.shareCollection(sharedCollection).resolveServiceOr(_ => showContactUsError())\n\n  private[this] def onError(e: Throwable) = e match {\n    case e: SharedCollectionsConfigurationException =>\n      AppLog.invalidConfigurationV2\n      showErrorLoadingCollectionInScreen()\n    case _ => showErrorLoadingCollectionInScreen()\n  }\n}\n\nobject PublicCollectionsFragment {\n\n  var statuses = PublicCollectionStatuses()\n\n  val serialCancelableTaskRef = SerialCancelable()\n}\n\ncase class PublicCollectionStatuses(\n    category: NineCardsCategory = Communication,\n    typeSharedCollection: TypeSharedCollection = TopSharedCollection) {\n\n  def reset: PublicCollectionStatuses =\n    copy(category = Communication, typeSharedCollection = TopSharedCollection)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/publicollections/PublicCollectionsJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.publicollections\n\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.ui.collections.tasks.CollectionJobs\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.app.ui.commons.dialogs.publicollections.PublicCollectionsFragment._\nimport cards.nine.commons.services.TaskService.{TaskService, _}\nimport cards.nine.models.{Collection, SharedCollection}\nimport cards.nine.models.types.{NineCardsCategory, TypeSharedCollection}\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ActivityContextWrapper\n\nimport cards.nine.commons.NineCardExtensions._\nimport cats.implicits._\n\nclass PublicCollectionsJobs(actions: PublicCollectionsUiActions)(\n    implicit contextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with Conversions\n    with CollectionJobs {\n\n  def initialize(): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.openPublicCollections()\n      _ <- actions.initialize()\n      _ <- loadPublicCollections()\n    } yield ()\n\n  def loadPublicCollections(): TaskService[Unit] = {\n\n    def getSharedCollections(\n        category: NineCardsCategory,\n        typeSharedCollection: TypeSharedCollection): TaskService[Seq[SharedCollection]] =\n      di.sharedCollectionsProcess.getSharedCollectionsByCategory(category, typeSharedCollection)\n\n    for {\n      _                 <- actions.showLoading()\n      sharedCollections <- getSharedCollections(statuses.category, statuses.typeSharedCollection)\n      _ <- if (sharedCollections.isEmpty) {\n        actions.showEmptyMessageInScreen()\n      } else {\n        actions.loadPublicCollections(sharedCollections)\n      }\n    } yield ()\n  }\n\n  def loadPublicCollectionsByCategory(category: NineCardsCategory): TaskService[Unit] = {\n    statuses = statuses.copy(category = category)\n    for {\n      _ <- actions.updateCategory(category)\n      _ <- loadPublicCollections()\n    } yield ()\n  }\n\n  def loadPublicCollectionsByTypeSharedCollection(\n      typeSharedCollection: TypeSharedCollection): TaskService[Unit] = {\n    statuses = statuses.copy(typeSharedCollection = typeSharedCollection)\n    for {\n      _ <- actions.updateTypeCollection(typeSharedCollection)\n      _ <- loadPublicCollections()\n    } yield ()\n  }\n\n  def saveSharedCollection(sharedCollection: SharedCollection): TaskService[Collection] = {\n\n    def addCollection() =\n      for {\n        collection <- addSharedCollection(sharedCollection)\n        _          <- actions.close()\n      } yield collection\n\n    di.sharedCollectionsProcess\n      .updateViewSharedCollection(sharedCollection.id)\n      .resolveLeftTo(()) *>\n      di.trackEventProcess\n        .createNewCollectionFromPublicCollection(sharedCollection.name)\n        .resolveLeftTo(()) *>\n      addCollection()\n\n  }\n\n  def shareCollection(sharedCollection: SharedCollection): TaskService[Unit] =\n    di.launcherExecutorProcess.launchShare(\n      getString(R.string.shared_collection_url, sharedCollection.id))\n\n  protected def getString(res: Int, formatArgs: scala.AnyRef*): String =\n    resGetString(res, formatArgs)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/publicollections/PublicCollectionsUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.publicollections\n\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.adapters.sharedcollections.SharedCollectionsAdapter\nimport cards.nine.app.ui.commons.dialogs.{BaseActionFragment, Styles}\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.commons.PaddingItemDecoration\nimport cards.nine.app.ui.components.layouts.tweaks.DialogToolbarTweaks._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.SharedCollection\nimport cards.nine.models.types._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.UIActionsExtras._\nimport macroid.extras.ViewTweaks._\n\ntrait PublicCollectionsUiActions extends Styles with AppNineCardsIntentConversions {\n\n  self: BaseActionFragment with PublicCollectionsDOM with PublicCollectionsListener =>\n\n  lazy val (categoryNamesMenu, categories) = {\n    val categoriesSorted = NineCardsCategory.appsCategories map { category =>\n      (resGetString(category.getStringResource) getOrElse category.name, category)\n    } sortBy (_._1)\n    (categoriesSorted map (_._1), categoriesSorted map (_._2))\n  }\n\n  def initialize(): TaskService[Unit] =\n    ((toolbar <~\n      dtbInit(colorPrimary) <~\n      dtbChangeText(R.string.publicCollections) <~\n      dtbExtended <~\n      dtbAddExtendedView(tabsMenu) <~\n      dtbNavigationOnClickListener((_) => unreveal())) ~\n      (typeFilter <~\n        On.click {\n          val values =\n            Seq(resGetString(R.string.top), resGetString(R.string.latest))\n          typeFilter <~ vListThemedPopupWindowShow(values = values, onItemClickListener = {\n            case 0 =>\n              loadPublicCollectionsByTypeSharedCollection(TopSharedCollection)\n            case _ =>\n              loadPublicCollectionsByTypeSharedCollection(LatestSharedCollection)\n          }, width = Some(resGetDimensionPixelSize(R.dimen.width_list_popup_menu)))\n        }) ~\n      (categoryFilter <~\n        On.click {\n          categoryFilter <~ vListThemedPopupWindowShow(\n            values = categoryNamesMenu,\n            onItemClickListener = (position: Int) => {\n              categories.lift(position) foreach loadPublicCollectionsByCategory\n            },\n            width = Some(resGetDimensionPixelSize(R.dimen.width_list_popup_menu)),\n            height = Some(resGetDimensionPixelSize(R.dimen.height_list_popup_menu)))\n        }) ~\n      (recycler <~\n        recyclerStyle <~\n        rvAddItemDecoration(new PaddingItemDecoration))).toService()\n\n  def showErrorLoadingCollectionInScreen(): TaskService[Unit] =\n    (showError() ~\n      showMessageInScreen(\n        R.string.errorLoadingPublicCollections,\n        error = true,\n        action = loadPublicCollections())).toService()\n\n  def showErrorSavingCollectionInScreen(): TaskService[Unit] =\n    (showError() ~\n      showMessageInScreen(\n        R.string.errorSavingPublicCollections,\n        error = true,\n        action = loadPublicCollections())).toService()\n\n  def showEmptyMessageInScreen(): TaskService[Unit] =\n    (showError() ~\n      showMessageInScreen(R.string.emptyPublicCollections, error = false, loadPublicCollections()))\n      .toService()\n\n  def showContactUsError(): TaskService[Unit] =\n    uiShortToast(R.string.contactUsError).toService()\n\n  def loadPublicCollections(sharedCollections: Seq[SharedCollection]): TaskService[Unit] = {\n    val adapter = SharedCollectionsAdapter(sharedCollections, onAddCollection, onShareCollection)\n    (showContent() ~\n      (recycler <~\n        rvLayoutManager(adapter.getLayoutManager) <~\n        rvAdapter(adapter))).toService()\n  }\n\n  def showLoading(): TaskService[Unit] =\n    ((loading <~ vVisible) ~ (recycler <~ vGone) ~ (errorContent <~ vGone)).toService()\n\n  def updateCategory(category: NineCardsCategory): TaskService[Unit] =\n    (categoryFilter <~ tvText(resGetString(category.getStringResource) getOrElse category.name))\n      .toService()\n\n  def updateTypeCollection(typeSharedCollection: TypeSharedCollection): TaskService[Unit] =\n    (typeSharedCollection match {\n      case TopSharedCollection    => typeFilter <~ tvText(R.string.top)\n      case LatestSharedCollection => typeFilter <~ tvText(R.string.latest)\n    }).toService()\n\n  def close(): TaskService[Unit] = unreveal().toService()\n\n  private[this] def showContent(): Ui[Any] =\n    (loading <~ vGone) ~ (recycler <~ vVisible) ~ (errorContent <~ vGone)\n\n  private[this] def showError(): Ui[Any] =\n    (loading <~ vGone) ~ (recycler <~ vGone) ~ (errorContent <~ vVisible)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/recommendations/RecommendationsAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.recommendations\n\nimport android.support.v7.widget.{LinearLayoutManager, RecyclerView}\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.styles.CollectionCardsStyles\nimport cards.nine.models.{NineCardsTheme, NotCategorizedPackage}\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\ncase class RecommendationsAdapter(\n    recommendations: Seq[NotCategorizedPackage],\n    onInstall: (NotCategorizedPackage) => Unit)(\n    implicit activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[ViewHolderRecommendationsLayoutAdapter] {\n\n  override def onCreateViewHolder(\n      parent: ViewGroup,\n      viewType: Int): ViewHolderRecommendationsLayoutAdapter = {\n    val view = LayoutInflater\n      .from(parent.getContext)\n      .inflate(R.layout.recommendations_item, parent, false)\n      .asInstanceOf[ViewGroup]\n    ViewHolderRecommendationsLayoutAdapter(view)\n  }\n\n  override def getItemCount: Int = recommendations.size\n\n  override def onBindViewHolder(\n      viewHolder: ViewHolderRecommendationsLayoutAdapter,\n      position: Int): Unit = {\n    val recommendation = recommendations(position)\n    viewHolder.bind(recommendation, onInstall).run\n  }\n\n  def getLayoutManager = new LinearLayoutManager(activityContext.application)\n\n}\n\ncase class ViewHolderRecommendationsLayoutAdapter(\n    content: ViewGroup)(implicit context: ActivityContextWrapper, val theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView\n    with CollectionCardsStyles {\n\n  lazy val root = findView(TR.recommendation_item_layout)\n\n  lazy val icon = findView(TR.recommendation_item_icon)\n\n  lazy val name = findView(TR.recommendation_item_name)\n\n  lazy val downloads = findView(TR.recommendation_item_downloads)\n\n  lazy val tag = findView(TR.recommendation_item_tag)\n\n  lazy val stars = findView(TR.recommendation_item_stars)\n\n  lazy val line = findView(TR.recommendation_item_line)\n\n  lazy val screenshots = Seq(\n    findView(TR.recommendation_item_screenshot1),\n    findView(TR.recommendation_item_screenshot2),\n    findView(TR.recommendation_item_screenshot3))\n\n  lazy val installNow = findView(TR.recommendation_item_install_now)\n\n  ((root <~ cardRootStyle) ~\n    (name <~ textStyle) ~\n    (line <~ vBackgroundColor(theme.getLineColor)) ~\n    (downloads <~ leftDrawableTextStyle(R.drawable.icon_download)) ~\n    (tag <~ textStyle) ~\n    (installNow <~ buttonStyle)).run\n\n  def bind(recommendedApp: NotCategorizedPackage, onInstall: (NotCategorizedPackage) => Unit)(\n      implicit uiContext: UiContext[_]): Ui[_] = {\n    val screensUi: Seq[Ui[_]] = (screenshots zip recommendedApp.screenshots) map {\n      case (view, screenshot) => view <~ ivUri(screenshot)\n    }\n    (icon <~ ivUri(recommendedApp.icon getOrElse \"\")) ~ // If icon don't exist ivUri will solve the problem\n      (stars <~ ivSrc(tintDrawable(getStarDrawable(recommendedApp.stars)))) ~\n      (name <~ tvText(recommendedApp.title)) ~\n      (downloads <~ tvText(recommendedApp.downloads)) ~\n      (tag <~ tvText(if (recommendedApp.free) resGetString(R.string.free) else \"\")) ~\n      Ui.sequence(screensUi: _*) ~\n      (installNow <~ On.click(Ui(onInstall(recommendedApp))))\n  }\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/recommendations/RecommendationsDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.recommendations\n\nimport cards.nine.models.NotCategorizedPackage\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\n\ntrait RecommendationsDOM { self: TypedFindView =>\n\n  lazy val recycler = findView(TR.actions_recycler)\n\n}\n\ntrait RecommendationsUiListener {\n\n  def loadRecommendations(): Unit\n\n  def addApp(app: NotCategorizedPackage): Unit\n\n  def installApp(app: NotCategorizedPackage): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/recommendations/RecommendationsFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.recommendations\n\nimport android.app.Dialog\nimport cards.nine.app.commons.{AppNineCardsIntentConversions, Conversions}\nimport cards.nine.app.ui.collections.jobs.{GroupCollectionsJobs, SingleCollectionJobs}\nimport cards.nine.app.ui.commons.AppLog\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.{TaskService, _}\nimport cards.nine.models.NotCategorizedPackage\nimport cards.nine.models.types.NineCardsCategory\nimport cards.nine.process.recommendations.RecommendedAppsConfigurationException\nimport com.fortysevendeg.ninecardslauncher.R\n\nclass RecommendationsFragment(\n    implicit groupCollectionsJobs: GroupCollectionsJobs,\n    singleCollectionJobs: Option[SingleCollectionJobs])\n    extends BaseActionFragment\n    with RecommendationsUiActions\n    with RecommendationsDOM\n    with RecommendationsUiListener\n    with Conversions\n    with AppNineCardsIntentConversions { self =>\n\n  lazy val nineCardCategory = NineCardsCategory(\n    getString(Seq(getArguments), RecommendationsFragment.categoryKey, \"\"))\n\n  lazy val packages =\n    getSeqString(Seq(getArguments), BaseActionFragment.packages, Seq.empty[String])\n\n  lazy val recommendationsJobs =\n    new RecommendationsJobs(nineCardCategory, packages, self)\n\n  override def getLayoutId: Int = R.layout.list_action_fragment\n\n  override protected lazy val backgroundColor: Int = loadBackgroundColor\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n    recommendationsJobs.initialize().resolveAsyncServiceOr(onError)\n  }\n\n  override def loadRecommendations(): Unit =\n    recommendationsJobs.loadRecommendations().resolveAsyncServiceOr(onError)\n\n  override def addApp(app: NotCategorizedPackage): Unit =\n    (for {\n      cards <- groupCollectionsJobs.addCards(Seq(toCardData(app)))\n      _ <- singleCollectionJobs match {\n        case Some(job) => job.addCards(cards)\n        case _         => TaskService.empty\n      }\n      _ <- recommendationsJobs.close()\n    } yield ()).resolveAsyncServiceOr(_ => recommendationsJobs.showError())\n\n  override def installApp(app: NotCategorizedPackage): Unit =\n    recommendationsJobs.installNow(app).resolveAsyncServiceOr(_ => recommendationsJobs.showError())\n\n  private[this] def onError(e: Throwable): TaskService[Unit] = e match {\n    case e: RecommendedAppsConfigurationException =>\n      AppLog.invalidConfigurationV2\n      recommendationsJobs.showErrorLoadingRecommendation()\n    case _ =>\n      recommendationsJobs.showErrorLoadingRecommendation()\n  }\n}\n\nobject RecommendationsFragment {\n  val categoryKey = \"category\"\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/recommendations/RecommendationsJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.recommendations\n\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.NotCategorizedPackage\nimport cards.nine.models.types.NineCardsCategory\nimport macroid.ActivityContextWrapper\n\nclass RecommendationsJobs(\n    category: NineCardsCategory,\n    packages: Seq[String],\n    actions: RecommendationsUiActions)(implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with AppNineCardsIntentConversions {\n\n  def initialize(): TaskService[Unit] =\n    for {\n      _ <- actions.initialize()\n      _ <- loadRecommendations()\n    } yield ()\n\n  def installNow(app: NotCategorizedPackage): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.addRecommendationByFab(app.packageName)\n      _ <- di.launcherExecutorProcess.launchGooglePlay(app.packageName)\n      _ <- actions.recommendationAdded(app)\n    } yield ()\n\n  def loadRecommendations(): TaskService[Unit] = {\n    for {\n      _ <- actions.showLoading()\n      recommendations <- if (category.isAppCategory) {\n        di.recommendationsProcess.getRecommendedAppsByCategory(category, packages)\n      } else {\n        di.recommendationsProcess.getRecommendedAppsByPackages(packages, packages)\n      }\n      _ <- actions.loadRecommendations(recommendations)\n    } yield ()\n  }\n\n  def showErrorLoadingRecommendation(): TaskService[Unit] =\n    actions.showErrorLoadingRecommendationInScreen()\n\n  def showError(): TaskService[Unit] = actions.showContactUsError()\n\n  def close(): TaskService[Unit] = actions.close()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/recommendations/RecommendationsUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.recommendations\n\nimport cards.nine.app.ui.commons.dialogs.{BaseActionFragment, Styles}\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.layouts.tweaks.DialogToolbarTweaks._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.NotCategorizedPackage\nimport cards.nine.models.types.theme.CardLayoutBackgroundColor\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.UIActionsExtras._\nimport macroid.extras.ViewTweaks._\n\ntrait RecommendationsUiActions extends Styles {\n\n  self: BaseActionFragment with RecommendationsDOM with RecommendationsUiListener =>\n\n  def loadBackgroundColor = theme.get(CardLayoutBackgroundColor)\n\n  def initialize(): TaskService[Unit] =\n    ((toolbar <~\n      dtbInit(colorPrimary) <~\n      dtbChangeText(R.string.recommendations) <~\n      dtbNavigationOnClickListener((_) => unreveal())) ~\n      (recycler <~ recyclerStyle)).toService()\n\n  def showLoading(): TaskService[Unit] =\n    ((loading <~ vVisible) ~ (recycler <~ vGone)).toService()\n\n  def showErrorLoadingRecommendationInScreen(): TaskService[Unit] =\n    showMessageInScreen(R.string.errorLoadingRecommendations, error = true, loadRecommendations())\n      .toService()\n\n  def loadRecommendations(recommendations: Seq[NotCategorizedPackage]): TaskService[Unit] = {\n    val adapter = RecommendationsAdapter(recommendations, installApp)\n    ((recycler <~\n      vVisible <~\n      rvLayoutManager(adapter.getLayoutManager) <~\n      rvAdapter(adapter)) ~\n      (loading <~ vGone)).toService()\n  }\n\n  def recommendationAdded(app: NotCategorizedPackage): TaskService[Unit] =\n    TaskService.right(addApp(app))\n\n  def close(): TaskService[Unit] = unreveal().toService()\n\n  def showContactUsError(): TaskService[Unit] =\n    uiShortToast(R.string.contactUsError).toService()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/shortcuts/ShortcutDialogAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.shortcuts\n\nimport android.support.v7.widget.{LinearLayoutManager, RecyclerView}\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport cards.nine.models.{NineCardsTheme, Shortcut}\nimport cards.nine.models.types.theme.DrawerTextColor\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.TextViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\ncase class ShortcutDialogAdapter(shortcuts: Seq[Shortcut], onConfigure: (Shortcut) => Unit)(\n    implicit activityContext: ActivityContextWrapper,\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[ViewHolderShortcutLayoutAdapter] {\n\n  override def onCreateViewHolder(\n      parent: ViewGroup,\n      viewType: Int): ViewHolderShortcutLayoutAdapter = {\n    val view = LayoutInflater\n      .from(parent.getContext)\n      .inflate(R.layout.shortcut_item, parent, false)\n      .asInstanceOf[ViewGroup]\n    ViewHolderShortcutLayoutAdapter(view)\n  }\n\n  override def getItemCount: Int = shortcuts.size\n\n  override def onBindViewHolder(viewHolder: ViewHolderShortcutLayoutAdapter, position: Int): Unit = {\n    val shortcut = shortcuts(position)\n    viewHolder.bind(shortcut, onConfigure).run\n  }\n\n  def getLayoutManager = new LinearLayoutManager(activityContext.application)\n\n}\n\ncase class ViewHolderShortcutLayoutAdapter(\n    content: ViewGroup)(implicit context: ActivityContextWrapper, theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView {\n\n  lazy val icon = Option(findView(TR.simple_item_icon))\n\n  lazy val name = Option(findView(TR.simple_item_name))\n\n  (name <~ tvColor(theme.get(DrawerTextColor))).run\n\n  def bind(shortcut: Shortcut, onConfigure: (Shortcut) => Unit): Ui[_] =\n    (content <~ On.click(Ui(onConfigure(shortcut)))) ~\n      (icon <~ (shortcut.icon map ivSrc getOrElse Tweak.blank)) ~\n      (name <~ tvText(shortcut.title))\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/shortcuts/ShortcutDialogDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.shortcuts\n\nimport cards.nine.app.ui.commons.RequestCodes._\nimport cards.nine.app.ui.commons.SafeUi._\nimport cards.nine.models.Shortcut\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\nimport macroid.{ActivityContextWrapper, Ui}\n\ntrait ShortcutDialogDOM { self: TypedFindView =>\n\n  lazy val recycler = findView(TR.actions_recycler)\n\n  def goToConfigureShortcut(shortcut: Shortcut)(\n      implicit activityContextWrapper: ActivityContextWrapper): Ui[Any] =\n    uiStartIntentForResult(shortcut.intent, shortcutAdded)\n\n}\n\ntrait ShortcutsUiListener {\n\n  def loadShortcuts(): Unit\n\n  def onConfigure(shortcut: Shortcut): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/shortcuts/ShortcutDialogFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.shortcuts\n\nimport android.app.Dialog\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.models.Shortcut\nimport com.fortysevendeg.ninecardslauncher.R\n\nclass ShortcutDialogFragment\n    extends BaseActionFragment\n    with ShortcutDialogUiActions\n    with ShortcutDialogDOM\n    with ShortcutsUiListener\n    with AppNineCardsIntentConversions { self =>\n\n  lazy val shortcutJobs = new ShortcutDialogJobs(self)\n\n  override def getLayoutId: Int = R.layout.list_action_fragment\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n    shortcutJobs.initialize().resolveAsyncServiceOr(_ => shortcutJobs.showErrorLoadingShortcuts())\n  }\n\n  override def loadShortcuts(): Unit =\n    shortcutJobs\n      .loadShortcuts()\n      .resolveAsyncServiceOr(_ => shortcutJobs.showErrorLoadingShortcuts())\n\n  def onConfigure(shortcut: Shortcut): Unit =\n    shortcutJobs.configureShortcut(shortcut).resolveAsync()\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/shortcuts/ShortcutDialogJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.shortcuts\n\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.commons.services.TaskService.{TaskService, _}\nimport cards.nine.models.Shortcut\nimport macroid.ActivityContextWrapper\n\nclass ShortcutDialogJobs(actions: ShortcutDialogUiActions)(\n    implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs {\n\n  def initialize(): TaskService[Unit] =\n    for {\n      _ <- actions.initialize()\n      _ <- loadShortcuts()\n    } yield ()\n\n  def loadShortcuts(): TaskService[Unit] =\n    for {\n      _         <- actions.showLoading()\n      shortcuts <- di.deviceProcess.getAvailableShortcuts\n      _         <- actions.loadShortcuts(shortcuts)\n    } yield ()\n\n  def configureShortcut(shortcut: Shortcut): TaskService[Unit] =\n    for {\n      _ <- actions.close()\n      _ <- actions.configureShortcut(shortcut)\n    } yield ()\n\n  def showErrorLoadingShortcuts(): TaskService[Unit] =\n    actions.showErrorLoadingShortcutsInScreen()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/shortcuts/ShortcutDialogUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.shortcuts\n\nimport cards.nine.app.ui.commons.dialogs.{BaseActionFragment, Styles}\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.layouts.tweaks.DialogToolbarTweaks._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.Shortcut\nimport cards.nine.models.types.theme.DrawerBackgroundColor\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.ViewTweaks._\n\nimport scala.math.Ordering.Implicits._\n\ntrait ShortcutDialogUiActions extends Styles {\n\n  self: BaseActionFragment with ShortcutDialogDOM with ShortcutsUiListener =>\n\n  def initialize(): TaskService[Unit] =\n    ((toolbar <~\n      dtbInit(colorPrimary) <~\n      dtbChangeText(R.string.shortcuts) <~\n      dtbNavigationOnClickListener((_) => unreveal())) ~\n      (recycler <~\n        recyclerStyle <~\n        vBackgroundColor(theme.get(DrawerBackgroundColor)))).toService()\n\n  def showLoading(): TaskService[Unit] =\n    ((loading <~ vVisible) ~ (recycler <~ vGone)).toService()\n\n  def close(): TaskService[Unit] = unreveal().toService()\n\n  def configureShortcut(shortcut: Shortcut): TaskService[Unit] =\n    goToConfigureShortcut(shortcut).toService()\n\n  def showErrorLoadingShortcutsInScreen(): TaskService[Unit] =\n    showMessageInScreen(R.string.errorLoadingShortcuts, error = true, loadShortcuts()).toService()\n\n  def loadShortcuts(shortcuts: Seq[Shortcut]): TaskService[Unit] = {\n    val sortedShortcuts = shortcuts sortBy sortByTitle\n    val adapter         = ShortcutDialogAdapter(sortedShortcuts, onConfigure)\n    ((recycler <~\n      vVisible <~\n      rvLayoutManager(adapter.getLayoutManager) <~\n      rvAdapter(adapter)) ~\n      (loading <~ vGone)).toService()\n  }\n\n  private[this] def sortByTitle(shortcut: Shortcut) =\n    shortcut.title map (c => if (c.isUpper) 2 * c + 1 else 2 * (c - ('a' - 'A')))\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/widgets/WidgetsAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.widgets\n\nimport android.support.v7.widget.{LinearLayoutManager, RecyclerView}\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.ops.WidgetsOps._\nimport cards.nine.models.AppWidget\nimport macroid.extras.TextViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport macroid.FullDsl._\nimport macroid._\n\ncase class WidgetsAdapter(\n    widgets: Seq[AppWidget],\n    widgetContentWidth: Int,\n    widgetContentHeight: Int,\n    onClick: (AppWidget => Unit))(\n    implicit activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_])\n    extends RecyclerView.Adapter[ViewHolderWidgetsLayoutAdapter] {\n\n  override def onCreateViewHolder(\n      parent: ViewGroup,\n      viewType: Int): ViewHolderWidgetsLayoutAdapter = {\n    val view = LayoutInflater.from(parent.getContext).inflate(TR.layout.widget_item, parent, false)\n    ViewHolderWidgetsLayoutAdapter(view)\n  }\n\n  override def getItemCount: Int = widgets.size\n\n  override def onBindViewHolder(viewHolder: ViewHolderWidgetsLayoutAdapter, position: Int): Unit = {\n    val publicCollection = widgets(position)\n    viewHolder.bind(publicCollection, widgetContentWidth, widgetContentHeight, onClick).run\n  }\n\n  def getLayoutManager = new LinearLayoutManager(activityContext.application)\n\n}\n\ncase class ViewHolderWidgetsLayoutAdapter(\n    content: ViewGroup)(implicit context: ActivityContextWrapper, uiContext: UiContext[_])\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView {\n\n  override protected def findViewById(id: Int): View = content.findViewById(id)\n\n  lazy val preview = findView(TR.widget_item_preview)\n\n  lazy val title = findView(TR.widget_item_title)\n\n  lazy val cells = findView(TR.widget_item_cells)\n\n  def bind(\n      widget: AppWidget,\n      widgetContentWidth: Int,\n      widgetContentHeight: Int,\n      onClick: (AppWidget => Unit)): Ui[Any] = {\n    val cell = widget.getCell(widgetContentWidth, widgetContentHeight)\n    val size = s\"${cell.spanX}x${cell.spanY}\"\n    val iconTweak =\n      if (widget.preview > 0)\n        ivSrcIconFromPackage(widget.packageName, widget.preview, widget.label)\n      else ivSrcByPackageName(Some(widget.packageName), widget.label)\n    (content <~\n      On.click(Ui(onClick(widget)))) ~\n      (preview <~ iconTweak) ~\n      (title <~ tvText(widget.label)) ~\n      (cells <~ tvText(size))\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/widgets/WidgetsDialogDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.widgets\n\nimport cards.nine.models.AppWidget\nimport com.fortysevendeg.ninecardslauncher.{TR, TypedFindView}\n\ntrait WidgetsDialogDOM { self: TypedFindView =>\n\n  lazy val recycler = findView(TR.widgets_actions_recycler)\n\n  lazy val menu = findView(TR.widgets_actions_menu)\n\n}\n\ntrait WidgetsDialogListener {\n\n  def loadWidgets(): Unit\n\n  def hostWidget(widget: AppWidget): Unit\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/widgets/WidgetsDialogJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.widgets\n\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.commons.services.TaskService._\nimport macroid.ActivityContextWrapper\n\nclass WidgetsDialogJobs(actions: WidgetsDialogUiActions)(\n    implicit contextWrapper: ActivityContextWrapper)\n    extends Jobs {\n\n  def initialize(): TaskService[Unit] =\n    for {\n      _ <- actions.initialize()\n      _ <- loadWidgets()\n    } yield ()\n\n  def loadWidgets(): TaskService[Unit] =\n    for {\n      _       <- actions.showLoading()\n      widgets <- di.deviceProcess.getWidgets\n      _       <- actions.loadWidgets(widgets)\n    } yield ()\n\n  def close(): TaskService[Unit] = actions.close()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/widgets/WidgetsDialogUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.widgets\n\nimport android.view.{Gravity, ViewGroup}\nimport android.widget.{ImageView, LinearLayout, TextView}\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.dialogs.{BaseActionFragment, Styles}\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.layouts.tweaks.DialogToolbarTweaks._\nimport cards.nine.app.ui.commons.dialogs.widgets.WidgetsFragment._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.{AppWidget, AppsWithWidgets}\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.LinearLayoutTweaks._\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\n\ntrait WidgetsDialogUiActions extends Styles {\n\n  self: BaseActionFragment with WidgetsDialogDOM with WidgetsDialogListener =>\n\n  val unselectedAlpha = .3f\n\n  val selectedAlpha = 1\n\n  lazy val padding = resGetDimensionPixelSize(R.dimen.padding_default)\n\n  def initialize(): TaskService[Unit] =\n    ((toolbar <~\n      dtbInit(colorPrimary) <~\n      dtbChangeText(R.string.widgetsTitle) <~\n      dtbNavigationOnClickListener((_) => unreveal())) ~\n      (recycler <~ recyclerStyle)).toService()\n\n  def loadWidgets(appsWithWidgets: Seq[AppsWithWidgets]): TaskService[Unit] = {\n    val (tag, widgets) = appsWithWidgets.headOption map (app =>\n                                                           (app.packageName, app.widgets)) getOrElse (\"\", Seq.empty)\n    val adapter = WidgetsAdapter(\n      Seq.empty,\n      statuses.widgetContentWidth,\n      statuses.widgetContentHeight,\n      hostWidget)\n    ((recycler <~\n      vVisible <~\n      rvLayoutManager(adapter.getLayoutManager) <~\n      rvAdapter(adapter)) ~\n      (loading <~ vGone) ~\n      loadMenuApps(appsWithWidgets) ~\n      showWidgets(tag, widgets)).toService()\n  }\n\n  def showLoading(): TaskService[Unit] =\n    ((loading <~ vVisible) ~ (recycler <~ vGone)).toService()\n\n  def showErrorLoadingWidgetsInScreen(): TaskService[Unit] =\n    showMessageInScreen(R.string.widgetsErrorMessage, error = true, action = loadWidgets())\n      .toService()\n\n  def close(): TaskService[Unit] = unreveal().toService()\n\n  private[this] def loadMenuApps(appsWithWidgets: Seq[AppsWithWidgets]): Ui[Any] = {\n\n    val views = appsWithWidgets map { app =>\n      (l[LinearLayout](\n        w[ImageView] <~ iconMenuItemStyle(app.packageName, app.name) <~ vTag(app.packageName),\n        w[TextView] <~ textMenuItemStyle(app.name) <~ vTag(app.packageName)) <~\n        contentMenuItemStyle <~\n        On.click(showWidgets(app.packageName, app.widgets))).get\n    }\n    menu <~ vgAddViews(views)\n  }\n\n  private[this] def showWidgets(tag: String, widgets: Seq[AppWidget]) =\n    recycler.getAdapter match {\n      case adapter: WidgetsAdapter =>\n        (recycler <~ rvSwapAdapter(adapter.copy(widgets))) ~\n          (menu <~ Transformer {\n            case content: ImageView if content.getTag == tag =>\n              content <~ vAlpha(selectedAlpha)\n            case content: ImageView =>\n              content <~ vAlpha(unselectedAlpha)\n            case content: TextView if content.getTag == tag =>\n              content <~ vAlpha(selectedAlpha)\n            case content: TextView =>\n              content <~ vAlpha(unselectedAlpha)\n          })\n      case _ => Ui.nop\n    }\n\n  // Styles\n\n  private[this] def contentMenuItemStyle: Tweak[LinearLayout] =\n    vWrapContent +\n      llHorizontal +\n      vPaddings(padding) +\n      llGravity(Gravity.CENTER_VERTICAL) +\n      vBackgroundColorResource(R.color.widgets_background_content)\n\n  private[this] def iconMenuItemStyle(packageName: String, name: String)(\n      implicit contextWrapper: ContextWrapper,\n      uiContext: UiContext[_]): Tweak[ImageView] = {\n    val size    = resGetDimensionPixelSize(R.dimen.size_widget_icon)\n    val padding = resGetDimensionPixelSize(R.dimen.padding_small)\n    lp[ViewGroup](size, size) +\n      vPaddings(padding) +\n      ivSrcByPackageName(Option(packageName), name)\n  }\n\n  private[this] def textMenuItemStyle(name: String)(\n      implicit contextWrapper: ContextWrapper): Tweak[TextView] =\n    vWrapContent +\n      tvColorResource(R.color.widgets_text) +\n      vPadding(paddingLeft = padding) +\n      vAlpha(unselectedAlpha) +\n      tvText(name) +\n      tvSizeResource(R.dimen.text_default)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/widgets/WidgetsFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.widgets\n\nimport android.app.Dialog\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\nimport cards.nine.app.ui.commons.dialogs.widgets.WidgetsFragment._\nimport cards.nine.app.ui.launcher.jobs.WidgetsJobs\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.AppWidget\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport cats.implicits._\n\nclass WidgetsFragment(implicit widgetsJobs: WidgetsJobs)\n    extends BaseActionFragment\n    with WidgetsDialogUiActions\n    with WidgetsDialogDOM\n    with WidgetsDialogListener\n    with AppNineCardsIntentConversions {\n\n  lazy val widgetContentWidth =\n    getString(Seq(getArguments), WidgetsFragment.widgetContentWidth, \"0\").toInt\n\n  lazy val widgetContentHeight =\n    getString(Seq(getArguments), WidgetsFragment.widgetContentHeight, \"0\").toInt\n\n  override def getLayoutId: Int = R.layout.widgets_action_fragment\n\n  override protected lazy val backgroundColor: Int = resGetColor(R.color.widgets_background)\n\n  lazy val widgetsDialogJobs = new WidgetsDialogJobs(this)\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n    statuses = statuses\n      .copy(widgetContentWidth = widgetContentWidth, widgetContentHeight = widgetContentHeight)\n    widgetsDialogJobs.initialize().resolveAsyncServiceOr(_ => showErrorLoadingWidgetsInScreen())\n  }\n\n  override def loadWidgets(): Unit =\n    widgetsDialogJobs.loadWidgets().resolveAsyncServiceOr(_ => showErrorLoadingWidgetsInScreen())\n\n  override def hostWidget(widget: AppWidget): Unit =\n    (widgetsJobs.hostWidget(widget) *> widgetsDialogJobs.close()).resolveAsync()\n}\n\nobject WidgetsFragment {\n\n  var statuses = WidgetStatuses()\n\n  val widgetContentWidth = \"widget-content-width\"\n\n  val widgetContentHeight = \"widget-content-height\"\n\n}\n\ncase class WidgetStatuses(widgetContentWidth: Int = 0, widgetContentHeight: Int = 0)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/wizard/WizardInlineDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.wizard\n\nimport android.view.ViewGroup\nimport com.fortysevendeg.ninecardslauncher.TR\n\nclass WizardInlineDOM(viewGroup: ViewGroup) {\n\n  import cards.nine.app.ui.commons.ViewGroupFindViews._\n\n  lazy val wizardInlineWorkspace =\n    findView(TR.wizard_inline_workspace).run(viewGroup)\n\n  lazy val wizardInlinePagination =\n    findView(TR.wizard_inline_pagination_panel).run(viewGroup)\n\n  lazy val wizardInlineSkip = findView(TR.wizard_inline_skip).run(viewGroup)\n\n  lazy val wizardInlineGotIt = findView(TR.wizard_inline_got_it).run(viewGroup)\n\n}\n\ntrait WizardListener {\n\n  def dismissWizard(): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/wizard/WizardInlineFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.wizard\n\nimport android.app.Dialog\nimport android.support.design.widget.BottomSheetBehavior.BottomSheetCallback\nimport android.support.design.widget.{\n  BottomSheetBehavior,\n  BottomSheetDialogFragment,\n  CoordinatorLayout\n}\nimport android.support.v4.app.Fragment\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport cards.nine.app.commons.ContextSupportProvider\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.{FragmentUiContext, UiContext, UiExtensions}\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.Contexts\n\nclass WizardInlineFragment\n    extends BottomSheetDialogFragment\n    with ContextSupportProvider\n    with UiExtensions\n    with WizardListener\n    with Contexts[Fragment] {\n\n  implicit lazy val uiContext: UiContext[Fragment] = FragmentUiContext(this)\n\n  lazy val wizardInlineType = WizardInlineType(\n    getString(Seq(getArguments), WizardInlineFragment.wizardInlineTypeKey, \"\"))\n\n  lazy val wizardInlinePreferences = new WizardInlinePreferences()\n\n  override def getTheme: Int = R.style.AppThemeDialog\n\n  override def onDestroy(): Unit = {\n    wizardInlinePreferences.wasShowed(wizardInlineType)\n    super.onDestroy()\n  }\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n\n    val baseView = LayoutInflater\n      .from(getActivity)\n      .inflate(R.layout.wizard_inline, javaNull, false)\n      .asInstanceOf[ViewGroup]\n\n    val uiActions =\n      new WizardInlineUiActions(new WizardInlineDOM(baseView), this)\n\n    uiActions.initialize(wizardInlineType).resolveAsync()\n\n    dialog.setContentView(baseView)\n\n    getBehaviour(baseView) foreach { behaviour =>\n      behaviour.setBottomSheetCallback(new BottomSheetCallback {\n        override def onSlide(view: View, slideOffset: Float): Unit = {}\n        override def onStateChanged(view: View, newState: Int): Unit =\n          if (newState == BottomSheetBehavior.STATE_HIDDEN) {\n            dismissWizard()\n          }\n      })\n      behaviour.setState(BottomSheetBehavior.STATE_EXPANDED)\n    }\n\n  }\n\n  override def dismissWizard(): Unit = {\n    wizardInlinePreferences.wasShowed(wizardInlineType)\n    dismiss()\n  }\n\n  private[this] def getBehaviour(viewGroup: ViewGroup): Option[BottomSheetBehavior[_]] =\n    viewGroup.getParent match {\n      case view: View =>\n        view.getLayoutParams match {\n          case params: CoordinatorLayout.LayoutParams =>\n            params.getBehavior match {\n              case behavior: BottomSheetBehavior[_] => Option(behavior)\n              case _                                => None\n            }\n          case _ => None\n        }\n      case _ => None\n    }\n\n}\n\nobject WizardInlineFragment {\n\n  val wizardInlineTypeKey = \"wizard-inline-type-key\"\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/wizard/WizardInlinePreferences.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.wizard\n\nimport android.content.Context\nimport macroid.ContextWrapper\n\nclass WizardInlinePreferences(implicit contextWrapper: ContextWrapper) {\n\n  private[this] val name = \"wizard-inline-preferences\"\n\n  private[this] lazy val wizardInlinePreferences =\n    contextWrapper.bestAvailable.getSharedPreferences(name, Context.MODE_PRIVATE)\n\n  def wasShowed(wizardInlineType: WizardInlineType): Unit =\n    wizardInlinePreferences.edit.putBoolean(wizardInlineType.toString, true).apply()\n\n  def shouldBeShowed(wizardInlineType: WizardInlineType): Boolean =\n    !wizardInlinePreferences.getBoolean(wizardInlineType.toString, false)\n\n  def clean(): Unit = wizardInlinePreferences.edit().clear().apply()\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/wizard/WizardInlineType.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.wizard\n\nsealed trait WizardInlineType\n\ncase object LauncherWizardInline extends WizardInlineType\n\ncase object AppDrawerWizardInline extends WizardInlineType\n\ncase object ProfileWizardInline extends WizardInlineType\n\ncase object CollectionsWizardInline extends WizardInlineType\n\nobject WizardInlineType {\n\n  def apply(name: String): WizardInlineType = name match {\n    case n if n == LauncherWizardInline.toString    => LauncherWizardInline\n    case n if n == AppDrawerWizardInline.toString   => AppDrawerWizardInline\n    case n if n == CollectionsWizardInline.toString => CollectionsWizardInline\n    case _                                          => ProfileWizardInline\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/dialogs/wizard/WizardInlineUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.dialogs.wizard\n\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.view.ViewGroup\nimport android.widget.ImageView\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.CommonsExcerpt._\nimport cards.nine.app.ui.components.layouts.WizardInlineData\nimport cards.nine.app.ui.components.layouts.tweaks.AnimatedWorkSpacesTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.WizardInlineWorkspacesTweaks._\nimport cards.nine.commons.services.TaskService.TaskService\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.LinearLayoutTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\n\nclass WizardInlineUiActions(dom: WizardInlineDOM, listener: WizardListener)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_]) {\n\n  def initialize(wizardInlineType: WizardInlineType): TaskService[Unit] = {\n\n    val steps = getSteps(wizardInlineType)\n\n    def pagination(position: Int) =\n      (w[ImageView] <~ paginationItemStyle <~ ivSrc(R.drawable.wizard_inline_pager) <~ vTag(\n        position.toString)).get\n\n    def createPagers() = {\n      val pagerViews = steps.indices map { position =>\n        val view = pagination(position)\n        view.setActivated(position == 0)\n        view\n      }\n      dom.wizardInlinePagination <~ vgAddViews(pagerViews)\n    }\n\n    def reloadPagers(currentPage: Int) = Transformer {\n      case i: ImageView if Option(i.getTag).isDefined && i.getTag.equals(currentPage.toString) =>\n        i <~ vActivated(true)\n      case i: ImageView => i <~ vActivated(false)\n    }\n\n    ((dom.wizardInlineWorkspace <~\n      vGlobalLayoutListener(_ => {\n        dom.wizardInlineWorkspace <~\n          wiwData(steps) <~\n          awsAddPageChangedObserver(currentPage => {\n            val showAction = currentPage == steps.length - 1\n            ((dom.wizardInlinePagination <~ reloadPagers(currentPage)) ~\n              ((showAction,\n                (dom.wizardInlineGotIt ~> isVisible).get,\n                (dom.wizardInlinePagination ~> isVisible).get) match {\n                case (true, false, _) =>\n                  (dom.wizardInlineGotIt <~ applyFadeIn()) ~\n                    (dom.wizardInlineSkip <~ applyFadeOut()) ~\n                    (dom.wizardInlinePagination <~ applyFadeOut())\n                case (false, _, false) =>\n                  (dom.wizardInlineGotIt <~ applyFadeOut()) ~\n                    (dom.wizardInlineSkip <~ applyFadeIn()) ~\n                    (dom.wizardInlinePagination <~ applyFadeIn())\n                case _ => Ui.nop\n              })).run\n          })\n      })) ~\n      (dom.wizardInlineSkip <~\n        On.click(Ui(listener.dismissWizard()))) ~\n      (dom.wizardInlineGotIt <~\n        vGone <~\n        On.click(Ui(listener.dismissWizard()))) ~\n      createPagers()).toService()\n  }\n\n  private[this] def getSteps(wizardInlineType: WizardInlineType) =\n    wizardInlineType match {\n      case AppDrawerWizardInline =>\n        Seq(\n          WizardInlineData(\n            R.drawable.wizard_inline_appdrawer_01,\n            resGetString(R.string.wizard_inline_appdrawer_title_1),\n            resGetString(R.string.wizard_inline_appdrawer_1)),\n          WizardInlineData(\n            R.drawable.wizard_inline_appdrawer_02,\n            resGetString(R.string.wizard_inline_appdrawer_title_2),\n            resGetString(R.string.wizard_inline_appdrawer_2)),\n          WizardInlineData(\n            R.drawable.wizard_inline_appdrawer_03,\n            resGetString(R.string.wizard_inline_appdrawer_title_3),\n            resGetString(R.string.wizard_inline_appdrawer_3)))\n      case LauncherWizardInline =>\n        Seq(\n          WizardInlineData(\n            R.drawable.wizard_inline_launcher_01,\n            resGetString(R.string.wizard_inline_launcher_title_1),\n            resGetString(R.string.wizard_inline_launcher_1)),\n          WizardInlineData(\n            R.drawable.wizard_inline_launcher_02,\n            resGetString(R.string.wizard_inline_launcher_title_2),\n            resGetString(R.string.wizard_inline_launcher_2)),\n          WizardInlineData(\n            R.drawable.wizard_inline_launcher_03,\n            resGetString(R.string.wizard_inline_launcher_title_3),\n            resGetString(R.string.wizard_inline_launcher_3)),\n          WizardInlineData(\n            R.drawable.wizard_inline_launcher_04,\n            resGetString(R.string.wizard_inline_launcher_title_4),\n            resGetString(R.string.wizard_inline_launcher_4)))\n      case CollectionsWizardInline =>\n        Seq(\n          WizardInlineData(\n            R.drawable.wizard_inline_collection_01,\n            resGetString(R.string.wizard_inline_collection_title_1),\n            resGetString(R.string.wizard_inline_collection_1)),\n          WizardInlineData(\n            R.drawable.wizard_inline_collection_02,\n            resGetString(R.string.wizard_inline_collection_title_2),\n            resGetString(R.string.wizard_inline_collection_2)),\n          WizardInlineData(\n            R.drawable.wizard_inline_collection_03,\n            resGetString(R.string.wizard_inline_collection_title_3),\n            resGetString(R.string.wizard_inline_collection_3)))\n      case ProfileWizardInline =>\n        Seq(\n          WizardInlineData(\n            R.drawable.wizard_inline_profile_01,\n            resGetString(R.string.wizard_inline_profile_title_1),\n            resGetString(R.string.wizard_inline_profile_1)),\n          WizardInlineData(\n            R.drawable.wizard_inline_profile_02,\n            resGetString(R.string.wizard_inline_profile_title_2),\n            resGetString(R.string.wizard_inline_profile_2)),\n          WizardInlineData(\n            R.drawable.wizard_inline_profile_03,\n            resGetString(R.string.wizard_inline_profile_title_3),\n            resGetString(R.string.wizard_inline_profile_3)))\n    }\n\n  // Styles\n\n  private[this] def paginationItemStyle(implicit context: ContextWrapper): Tweak[ImageView] = {\n    val size   = resGetDimensionPixelSize(R.dimen.wizard_size_pager)\n    val margin = resGetDimensionPixelSize(R.dimen.wizard_margin_pager)\n    lp[ViewGroup](size, size) +\n      llLayoutMargin(margin, margin, margin, margin)\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/glide/AppIconLoader.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.glide\n\nimport com.bumptech.glide.Priority\nimport com.bumptech.glide.load.data.DataFetcher\nimport com.bumptech.glide.load.model.ModelLoader\n\nclass AppIconLoader extends ModelLoader[String, String] {\n\n  override def getResourceFetcher(model: String, width: Int, height: Int): DataFetcher[String] =\n    new DataFetcher[String]() {\n      override def cleanup(): Unit = {}\n\n      override def loadData(priority: Priority): String = model\n\n      override def cancel(): Unit = {}\n\n      override def getId: String = model\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/glide/ApplicationIconDecoder.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.glide\n\nimport android.graphics.Bitmap\nimport android.graphics.drawable.BitmapDrawable\nimport com.bumptech.glide.Glide\nimport com.bumptech.glide.load.ResourceDecoder\nimport com.bumptech.glide.load.engine.Resource\nimport com.bumptech.glide.load.resource.bitmap.BitmapResource\nimport com.bumptech.glide.util.Util\nimport macroid.ContextWrapper\n\nclass ApplicationIconDecoder(packageName: String)(implicit contextWrapper: ContextWrapper)\n    extends ResourceDecoder[String, Bitmap] {\n\n  override def getId: String = packageName\n\n  override def decode(source: String, width: Int, height: Int): Resource[Bitmap] = {\n\n    val icon =\n      contextWrapper.application.getPackageManager\n        .getApplicationIcon(packageName)\n        .asInstanceOf[BitmapDrawable]\n        .getBitmap\n\n    val pool = Glide.get(contextWrapper.bestAvailable).getBitmapPool\n\n    new BitmapResource(icon, pool) {\n      override def getSize: Int = Util.getBitmapByteSize(icon)\n\n      override def recycle(): Unit = {}\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/glide/IconFromPackageDecoder.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.glide\n\nimport android.annotation.SuppressLint\nimport android.graphics.Bitmap\nimport android.graphics.drawable.BitmapDrawable\nimport com.bumptech.glide.Glide\nimport com.bumptech.glide.load.ResourceDecoder\nimport com.bumptech.glide.load.engine.Resource\nimport com.bumptech.glide.load.resource.bitmap.BitmapResource\nimport com.bumptech.glide.util.Util\nimport macroid.extras.DeviceVersion.Marshmallow\nimport macroid.ContextWrapper\n\n@SuppressLint(Array(\"NewApi\"))\nclass IconFromPackageDecoder(packageName: String)(implicit contextWrapper: ContextWrapper)\n    extends ResourceDecoder[Int, Bitmap] {\n\n  override def getId: String = packageName\n\n  override def decode(source: Int, width: Int, height: Int): Resource[Bitmap] = {\n\n    val resources =\n      contextWrapper.application.getPackageManager.getResourcesForApplication(packageName)\n    val icon = Marshmallow ifSupportedThen {\n      resources.getDrawable(source, null).asInstanceOf[BitmapDrawable].getBitmap\n    } getOrElse {\n      resources.getDrawable(source).asInstanceOf[BitmapDrawable].getBitmap\n    }\n\n    val pool = Glide.get(contextWrapper.bestAvailable).getBitmapPool\n\n    new BitmapResource(icon, pool) {\n      override def getSize: Int = Util.getBitmapByteSize(icon)\n\n      override def recycle(): Unit = {}\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/glide/IconFromPackageLoader.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.glide\n\nimport com.bumptech.glide.Priority\nimport com.bumptech.glide.load.data.DataFetcher\nimport com.bumptech.glide.load.model.ModelLoader\n\nclass IconFromPackageLoader extends ModelLoader[Int, Int] {\n\n  override def getResourceFetcher(model: Int, width: Int, height: Int): DataFetcher[Int] =\n    new DataFetcher[Int]() {\n      override def cleanup(): Unit = {}\n\n      override def loadData(priority: Priority): Int = model\n\n      override def cancel(): Unit = {}\n\n      override def getId: String = model.toString\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/google_api/GoogleApiClientProvider.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.google_api\n\nimport android.os.Bundle\nimport cards.nine.app.ui.commons.RequestCodes._\nimport cards.nine.app.ui.commons.SafeUi._\nimport com.fortysevendeg.ninecardslauncher.R\nimport com.google.android.gms.auth.api.Auth\nimport com.google.android.gms.auth.api.signin.GoogleSignInOptions\nimport com.google.android.gms.common.ConnectionResult\nimport com.google.android.gms.common.api.{GoogleApiClient, Scope}\nimport com.google.android.gms.drive._\nimport com.google.android.gms.plus.Plus\nimport macroid.{ActivityContextWrapper, ContextWrapper}\n\n/**\n * @deprecated use the CloudStorageProcess.createCloudStorageClient instead\n */\ntrait GoogleDriveApiClientProvider {\n\n  def createGoogleDriveClient(account: String)(\n      implicit contextWrapper: ContextWrapper): GoogleApiClient =\n    new GoogleApiClient.Builder(contextWrapper.bestAvailable)\n      .setAccountName(account)\n      .addApi(Drive.API)\n      .addScope(Drive.SCOPE_APPFOLDER)\n      .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks {\n        override def onConnectionSuspended(cause: Int): Unit =\n          onDriveConnectionSuspended(ConnectionSuspendedCause(cause))\n\n        override def onConnected(bundle: Bundle): Unit =\n          onDriveConnected(bundle)\n      })\n      .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener {\n        override def onConnectionFailed(connectionResult: ConnectionResult): Unit =\n          onDriveConnectionFailed(connectionResult)\n      })\n      .build()\n\n  def onDriveConnectionSuspended(connectionSuspendedCause: ConnectionSuspendedCause): Unit\n\n  def onDriveConnected(bundle: Bundle): Unit\n\n  def onDriveConnectionFailed(connectionResult: ConnectionResult): Unit\n\n}\n\n/**\n * @deprecated use the SocialProfileProcess.createSocialProfileClient instead\n */\ntrait GooglePlusApiClientProvider {\n\n  def createGooglePlusClient(account: String)(\n      implicit contextWrapper: ContextWrapper): GoogleApiClient = {\n    val gso =\n      new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)\n        .requestScopes(Plus.SCOPE_PLUS_PROFILE)\n        .requestIdToken(contextWrapper.bestAvailable.getString(R.string.api_v2_client_id))\n        .setAccountName(account)\n        .build()\n\n    new GoogleApiClient.Builder(contextWrapper.bestAvailable)\n      .addApi(Auth.GOOGLE_SIGN_IN_API, gso)\n      .addApi(Plus.API)\n      .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks {\n        override def onConnectionSuspended(cause: Int): Unit =\n          onPlusConnectionSuspended(ConnectionSuspendedCause(cause))\n\n        override def onConnected(bundle: Bundle): Unit =\n          onPlusConnected(bundle)\n      })\n      .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener {\n        override def onConnectionFailed(connectionResult: ConnectionResult): Unit =\n          onPlusConnectionFailed(connectionResult)\n      })\n      .build()\n  }\n\n  def onPlusConnectionSuspended(connectionSuspendedCause: ConnectionSuspendedCause): Unit\n\n  def onPlusConnected(bundle: Bundle): Unit\n\n  def onPlusConnectionFailed(connectionResult: ConnectionResult): Unit\n\n}\n\nsealed trait ConnectionSuspendedCause\n\ncase object CauseNetworkLost extends ConnectionSuspendedCause\n\ncase object CauseServiceDisconnected extends ConnectionSuspendedCause\n\ncase object CauseUnknown extends ConnectionSuspendedCause\n\nobject ConnectionSuspendedCause {\n\n  def apply(cause: Int): ConnectionSuspendedCause =\n    cause match {\n      case GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST =>\n        CauseNetworkLost\n      case GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED =>\n        CauseServiceDisconnected\n      case _ => CauseUnknown\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/CollectionOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport cards.nine.models.{Collection, CollectionData}\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nobject CollectionOps {\n\n  implicit class CollectionOp(collection: Collection) {\n\n    def getUrlSharedCollection(implicit contextWrapper: ContextWrapper): Option[String] =\n      collection.sharedCollectionId map { c =>\n        resGetString(R.string.shared_collection_url, c)\n      }\n\n    def getUrlOriginalSharedCollection(implicit contextWrapper: ContextWrapper): Option[String] =\n      collection.originalSharedCollectionId map { c =>\n        resGetString(R.string.shared_collection_url, c)\n      }\n\n    def getIconWorkspace(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${collection.icon.toLowerCase}\") getOrElse R.drawable.icon_collection_default\n\n    def getIconDetail(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${collection.icon.toLowerCase}_detail\") getOrElse R.drawable.icon_collection_default_detail\n\n  }\n\n  implicit class CollectionStringsOp(icon: String) {\n\n    def getIconWorkspace(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${icon.toLowerCase}\") getOrElse R.drawable.icon_collection_default\n\n    def getIconDetail(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${icon.toLowerCase}_detail\") getOrElse R.drawable.icon_collection_default_detail\n\n  }\n\n  implicit class CollectionDataOp(collection: CollectionData) {\n\n    def getIconCollectionWorkspace(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${collection.icon.toLowerCase}\") getOrElse R.drawable.icon_collection_default\n\n    def getIconCollectionDetail(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${collection.icon.toLowerCase}_detail\") getOrElse R.drawable.icon_collection_default_detail\n\n    def getName(implicit contextWrapper: ContextWrapper): String =\n      resGetString(collection.name.toLowerCase) getOrElse collection.name\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/ConditionWeatherOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport cards.nine.models.types._\nimport com.fortysevendeg.ninecardslauncher.R\n\nobject ConditionWeatherOps {\n\n  implicit class ConditionWeatherIcon(condition: ConditionWeather) {\n\n    def getIcon: Int = condition match {\n      case ClearCondition   => R.drawable.icon_weather_clear\n      case CloudyCondition  => R.drawable.icon_weather_cloudy\n      case FoggyCondition   => R.drawable.icon_weather_foggy\n      case HazyCondition    => R.drawable.icon_weather_hazy\n      case IcyCondition     => R.drawable.icon_weather_icy\n      case RainyCondition   => R.drawable.icon_weather_rainy\n      case SnowyCondition   => R.drawable.icon_weather_snowy\n      case StormyCondition  => R.drawable.icon_weather_stormy\n      case WindyCondition   => R.drawable.icon_weather_windy\n      case UnknownCondition => R.drawable.icon_weather_unknown\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/DrawableOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport android.graphics.drawable.Drawable\nimport android.support.v4.graphics.drawable.DrawableCompat\n\nobject DrawableOps {\n\n  implicit class DrawableColors(drawable: Drawable) {\n\n    def colorize(color: Int) = {\n      val colorizeDrawable = DrawableCompat.wrap(drawable).mutate()\n      DrawableCompat.setTint(DrawableCompat.wrap(colorizeDrawable).mutate(), color)\n      colorizeDrawable\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/NineCardsCategoryOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport cards.nine.models.types.NineCardsCategory\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nobject NineCardsCategoryOps {\n\n  implicit class NineCardCategoryOp(nineCardCategory: NineCardsCategory) {\n\n    def getIconCollectionWorkspace(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${nineCardCategory.getIconResource}\") getOrElse R.drawable.icon_collection_default\n\n    def getIconCollectionDetail(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${nineCardCategory.getIconResource}_detail\") getOrElse R.drawable.icon_collection_default_detail\n\n    def getName(implicit contextWrapper: ContextWrapper): String =\n      resGetString(nineCardCategory.getStringResource) getOrElse nineCardCategory.name\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/NineCardsMomentOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport cards.nine.models.types.NineCardsMoment\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nobject NineCardsMomentOps {\n\n  implicit class NineCardsMomentOp(nineCardsMoment: NineCardsMoment) {\n\n    def getIconCollectionWorkspace(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${nineCardsMoment.getIconResource}\") getOrElse R.drawable.icon_collection_default\n\n    def getIconCollectionDetail(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${nineCardsMoment.getIconResource}_detail\") getOrElse R.drawable.icon_collection_default_detail\n\n    def getName(implicit contextWrapper: ContextWrapper): String =\n      resGetString(nineCardsMoment.getStringResource) getOrElse nineCardsMoment.name\n\n    def getDescription(implicit contextWrapper: ContextWrapper): String =\n      resGetString(s\"${nineCardsMoment.getStringResource}Description\") getOrElse nineCardsMoment.name\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/ResourcesCollectionDataOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport cards.nine.models.CollectionData\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nobject ResourcesCollectionDataOps {\n\n  implicit class ResourcesCollectionDataOp(privateCollection: CollectionData) {\n\n    def getIconCollectionWorkspace(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${privateCollection.icon.toLowerCase}\") getOrElse R.drawable.icon_collection_default\n\n    def getIconCollectionDetail(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${privateCollection.icon.toLowerCase}_detail\") getOrElse R.drawable.icon_collection_default_detail\n\n    def getName(implicit contextWrapper: ContextWrapper): String =\n      resGetString(privateCollection.name.toLowerCase) getOrElse privateCollection.name\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/SharedCollectionOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport cards.nine.models.SharedCollection\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nobject SharedCollectionOps {\n\n  implicit class PrivateCollectionOp(sharedCollection: SharedCollection) {\n\n    def getIconCollectionWorkspace(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${sharedCollection.icon.toLowerCase}\") getOrElse R.drawable.icon_collection_default\n\n    def getIconCollectionDetail(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${sharedCollection.icon.toLowerCase}_detail\") getOrElse R.drawable.icon_collection_default_detail\n\n    def getName(implicit contextWrapper: ContextWrapper): String =\n      resGetString(sharedCollection.name.toLowerCase) getOrElse sharedCollection.name\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/SubscriptionOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport cards.nine.models.Subscription\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nobject SubscriptionOps {\n\n  implicit class SubscriptionOp(subscription: Subscription) {\n\n    def getIconSubscriptionDetail(implicit context: ContextWrapper): Int =\n      resGetDrawableIdentifier(s\"icon_collection_${subscription.icon.toLowerCase}_detail\") getOrElse R.drawable.icon_collection_default_detail\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/TaskServiceOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.{NineCardException, TaskService}\nimport cards.nine.app.ui.commons.AppLog._\nimport monix.eval.Task\nimport cats.syntax.either._\nimport monix.execution.Cancelable\nimport monix.execution.Scheduler.Implicits.global\n\nimport scala.concurrent.Await\nimport scala.concurrent.duration._\nimport scala.util.{Either, Failure, Success}\n\nobject TaskServiceOps {\n\n  implicit class TaskServiceUi[A](t: TaskService[A]) {\n\n    // TODO - Do not use, it's only used in the `getTheme`. Remove as part of #808\n    def resolveNow: Either[NineCardException, A] =\n      Await.result(t.value.runAsync, 10.seconds)\n\n    def resolveAsync[E >: Throwable](\n        onResult: A => Unit = a => (),\n        onException: E => Unit = (e: Throwable) => ()\n    ): Cancelable = {\n      Task.fork(t.value).runAsync { result =>\n        result match {\n          case Failure(ex) =>\n            printErrorTaskMessage(\"=> EXCEPTION Disjunction <=\", ex)\n            onException(ex)\n          case Success(Right(value)) => onResult(value)\n          case Success(Left(ex)) =>\n            printErrorTaskMessage(s\"=> EXCEPTION Left) <=\", ex)\n            onException(ex)\n        }\n      }\n    }\n\n    def resolveAsyncDelayed[E >: Throwable](\n        finiteDuration: FiniteDuration,\n        onResult: A => Unit = a => (),\n        onException: E => Unit = (e: Throwable) => ()\n    ): Cancelable = {\n      Task.fork(t.value.delayExecution(finiteDuration)).runAsync { result =>\n        result match {\n          case Failure(ex) =>\n            printErrorTaskMessage(\"=> EXCEPTION Disjunction <=\", ex)\n            onException(ex)\n          case Success(Right(value)) => onResult(value)\n          case Success(Left(ex)) =>\n            printErrorTaskMessage(s\"=> EXCEPTION Left) <=\", ex)\n            onException(ex)\n        }\n      }\n    }\n\n    def resolveAutoCancelableAsyncDelayed[E >: Throwable](\n        finiteDuration: FiniteDuration,\n        onResult: A => Unit = a => (),\n        onException: E => Unit = (e: Throwable) => ()\n    ): Cancelable = {\n      Task\n        .defer(t.value)\n        .executeWithOptions(_.enableAutoCancelableRunLoops)\n        .delayExecution(finiteDuration)\n        .runAsync { result =>\n          result match {\n            case Failure(ex) =>\n              printErrorTaskMessage(\"=> EXCEPTION Disjunction <=\", ex)\n              onException(ex)\n            case Success(Right(value)) => onResult(value)\n            case Success(Left(ex)) =>\n              printErrorTaskMessage(s\"=> EXCEPTION Left) <=\", ex)\n              onException(ex)\n          }\n        }\n    }\n\n    def resolveAsyncService[E >: Throwable](\n        onResult: (A) => TaskService[A] = a => TaskService(Task(Either.right(a))),\n        onException: (E) => TaskService[A] = (e: NineCardException) =>\n          TaskService(Task(Either.left(e)))): Cancelable = {\n      Task.fork(t.value).runAsync { result =>\n        result match {\n          case Failure(ex) =>\n            printErrorTaskMessage(\"=> EXCEPTION Disjunction <=\", ex)\n            onException(ex).value.runAsync\n          case Success(Right(response)) => onResult(response).value.coeval\n          case Success(Left(ex)) =>\n            printErrorTaskMessage(s\"=> EXCEPTION Left) <=\", ex)\n            onException(ex).value.runAsync\n        }\n      }\n    }\n\n    def resolveAutoCancelableAsyncService[E >: Throwable](\n        onResult: (A) => TaskService[A] = a => TaskService(Task(Either.right(a))),\n        onException: (E) => TaskService[A] = (e: NineCardException) =>\n          TaskService(Task(Either.left(e)))): Cancelable = {\n      Task.defer(t.value).executeWithOptions(_.enableAutoCancelableRunLoops).runAsync { result =>\n        result match {\n          case Failure(ex) =>\n            printErrorTaskMessage(\"=> EXCEPTION Disjunction <=\", ex)\n            onException(ex).value.runAsync\n          case Success(Right(response)) => onResult(response).value.coeval\n          case Success(Left(ex)) =>\n            printErrorTaskMessage(s\"=> EXCEPTION Left) <=\", ex)\n            onException(ex).value.runAsync\n        }\n      }\n    }\n\n    def resolve[E >: Throwable](\n        onResult: A => Unit = a => (),\n        onException: E => Unit = (e: Throwable) => ()): Unit = {\n      t.value.map {\n        case Right(response) => onResult(response)\n        case Left(ex) =>\n          printErrorTaskMessage(\"=> EXCEPTION Left <=\", ex)\n          onException(ex)\n      }.coeval.runAttempt\n    }\n\n    def resolveService[E >: Throwable](\n        onResult: (A) => TaskService[A] = a => TaskService(Task(Either.right(a))),\n        onException: (E) => TaskService[A] = (e: NineCardException) =>\n          TaskService(Task(Either.left(e)))): Unit = {\n      Task\n        .fork(t.value)\n        .map {\n          case Right(response) => onResult(response).value.coeval.runAttempt\n          case Left(ex) =>\n            printErrorTaskMessage(\"=> EXCEPTION Left <=\", ex)\n            onException(ex).value.coeval.runAttempt\n        }\n        .coeval\n        .runAttempt\n    }\n\n    def resolveServiceOr[E >: Throwable](exception: (E) => TaskService[A]) =\n      resolveService(onException = exception)\n\n    def resolveAsyncServiceOr[E >: Throwable](exception: (E) => TaskService[A]): Cancelable =\n      resolveAsyncService(onException = exception)\n\n    def resolveAutoCancelableAsyncServiceOr[E >: Throwable](\n        exception: (E) => TaskService[A]): Cancelable =\n      resolveAutoCancelableAsyncService(onException = exception)\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/TryOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport cards.nine.app.ui.commons.AppLog\n\nimport scala.util.{Failure, Try}\n\nobject TryOps {\n\n  implicit class FailureLogOps[T](t: Try[T]) {\n\n    val tag = AppLog.tag\n\n    def logInfo(): Unit = log(Info)\n\n    def logDebug(): Unit = log(Debug)\n\n    def logError(): Unit = log(Error)\n\n    def logVerbose(): Unit = log(Verbose)\n\n    def log(level: LogLevel): Unit =\n      t match {\n        case Failure(e) =>\n          Option(e.getMessage) foreach { m =>\n            level match {\n              case Info    => android.util.Log.i(tag, m, e)\n              case Debug   => android.util.Log.d(tag, m, e)\n              case Error   => android.util.Log.e(tag, m, e)\n              case Verbose => android.util.Log.v(tag, m, e)\n            }\n          }\n        case _ =>\n      }\n\n  }\n\n  sealed trait LogLevel\n\n  case object Info extends LogLevel\n\n  case object Debug extends LogLevel\n\n  case object Error extends LogLevel\n\n  case object Verbose extends LogLevel\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/UiOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport android.view.View\nimport cards.nine.app.ui.commons.UiException\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cats.syntax.either._\nimport macroid.Ui\nimport monix.eval.Task\n\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.Future\n\nobject UiOps {\n\n  implicit class OptionViewOp[T <: View](maybeView: Option[T]) {\n\n    def mapUi(f: (T) => Ui[_]): Ui[_] = maybeView map f getOrElse Ui.nop\n\n    def mapUiF(f: (T) => Ui[Future[_]]): Ui[Future[_]] =\n      maybeView map f getOrElse Ui(Future.successful(()))\n\n    def ifUi[T](doUi: Boolean)(ui: () => Ui[T]): Ui[_] =\n      if (doUi) ui() else Ui.nop\n\n  }\n\n  implicit class UiActionsOp(ui: Ui[_]) {\n\n    def ifUi[T](doUi: Boolean) = if (doUi) ui else Ui.nop\n\n  }\n\n  implicit class UiActionsFutureOp(ui: Ui[Future[_]]) {\n\n    def ifUi[T](doUi: Boolean) = if (doUi) ui else Ui(Future.successful(()))\n\n  }\n\n  implicit class ServiceUi(ui: Ui[Any]) {\n\n    def toService(errorMessage: Option[String] = None): TaskService[Unit] =\n      TaskService {\n        Task.defer {\n          Task.fromFuture {\n            ui.run map { _ =>\n              Either.right[UiException, Unit](())\n            } recover {\n              case ex: Throwable =>\n                Either.left(UiException(errorMessage getOrElse \"Ui Exception\", Option(ex)))\n            }\n          }\n        }\n      }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/ViewGroupOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport android.view.{View, ViewGroup}\n\nobject ViewGroupOps {\n\n  implicit class ViewGroupExtras(view: ViewGroup) {\n\n    def children: Seq[View] =\n      (0 until view.getChildCount) map (position => view.getChildAt(position))\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/ViewOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport android.view.View\nimport com.fortysevendeg.ninecardslauncher.R\n\nobject ViewOps {\n\n  val positionId = R.id.position\n\n  val viewTypeId = R.id.view_type\n\n  val useLayerHardwareId = R.id.use_layer_hardware\n\n  val runningAnimation = R.id.running_animation\n\n  val fieldsMap = R.id.fields_map\n\n  implicit class ViewExtras(view: View) {\n\n    def isPosition(item: Int): Boolean =\n      Option(view.getTag(positionId)) exists (tag => Int.unbox(tag).equals(item))\n\n    def isRunningAnimation: Boolean =\n      Option(view.getTag(runningAnimation)) exists Boolean.unbox\n\n    def setRunningAnimation(running: Boolean): Unit =\n      view.setTag(runningAnimation, running)\n\n    def getFieldsMap: Map[String, Any] =\n      Option(view.getTag(fieldsMap)) map (_.asInstanceOf[Map[String, Any]]) getOrElse Map.empty\n\n    def getField[T](key: String): Option[T] =\n      getFieldsMap find (_._1 == key) map (_._2.asInstanceOf[T])\n\n    def getType: Option[String] =\n      Option(view.getTag(viewTypeId)) map (_.toString)\n\n    def isType(t: String): Boolean =\n      Option(view.getTag(viewTypeId)).isDefined && view.getTag(viewTypeId).equals(t)\n\n    def getPosition: Option[Int] =\n      Option(view.getTag(positionId)) map (pos => Int.unbox(pos))\n\n    def hasLayerHardware: Boolean =\n      Option(view.getTag(useLayerHardwareId)).isDefined\n\n    def calculateAnchorViewPosition: (Int, Int) = {\n      val loc = new Array[Int](2)\n      view.getLocationOnScreen(loc)\n      (loc(0), loc(1))\n    }\n\n    def projectionScreenPositionInView(x: Int, y: Int): (Int, Int) = {\n      val loc = new Array[Int](2)\n      view.getLocationOnScreen(loc)\n      (x - loc(0), y - loc(1))\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/ops/WidgetsOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.ops\n\nimport android.appwidget.AppWidgetProviderInfo\nimport cards.nine.app.ui.commons.SystemBarsTint\nimport cards.nine.models.{AppWidget, Widget, WidgetArea}\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.{ActivityContextWrapper, ContextWrapper}\n\nobject WidgetsOps {\n\n  val rows = 5\n\n  val columns = 5\n\n  def sizeCell(widthContent: Int, heightContent: Int)(\n      implicit contextWrapper: ContextWrapper): (Int, Int) = {\n    val padding = resGetDimensionPixelSize(R.dimen.padding_default)\n    val widthW  = widthContent - (padding * 2)\n    val heightW = heightContent - (padding * 2)\n    (widthW / columns, heightW / rows)\n  }\n\n  def dimensionToCell(widthContent: Int, heightContent: Int, minWidth: Int, minHeight: Int)(\n      implicit contextWrapper: ContextWrapper): Cell = {\n    val padding = resGetDimensionPixelSize(R.dimen.padding_default)\n\n    val widthW  = widthContent - (padding * 2)\n    val heightW = heightContent - (padding * 2)\n\n    val smallestCellWidth  = (widthW / columns).toFloat\n    val smallestCellHeight = (heightW / rows).toFloat\n\n    val widthWidget  = minWidth.toFloat + (padding * 2)\n    val heightWidget = minHeight.toFloat + (padding * 2)\n\n    val spanX = math.max(1, math.ceil(widthWidget / smallestCellWidth).toInt)\n    val spanY = math.max(1, math.ceil(heightWidget / smallestCellHeight).toInt)\n\n    Cell(spanX, spanY, smallestCellWidth.toInt, smallestCellHeight.toInt)\n  }\n\n  implicit class AppWidgetProviderInfoOp(info: AppWidgetProviderInfo) {\n\n    def getCell(widthContent: Int, heightContent: Int)(\n        implicit contextWrapper: ContextWrapper): Cell =\n      dimensionToCell(\n        widthContent = widthContent,\n        heightContent = heightContent,\n        minWidth = info.minWidth,\n        minHeight = info.minHeight)\n\n  }\n\n  implicit class WidgetAreaOp(area: WidgetArea) {\n\n    def getCell(widthContent: Int, heightContent: Int)(\n        implicit contextWrapper: ContextWrapper): Cell =\n      Cell(area.spanX, area.spanY, widthContent, heightContent)\n\n  }\n\n  implicit class AppWidgetOp(widget: AppWidget) {\n\n    def getCell(widthContent: Int, heightContent: Int)(\n        implicit contextWrapper: ContextWrapper): Cell =\n      dimensionToCell(\n        widthContent = widthContent,\n        heightContent = heightContent,\n        minWidth = widget.minWidth,\n        minHeight = widget.minHeight)\n\n    def getSimulateCell(implicit contextWrapper: ActivityContextWrapper): Cell = {\n      val systemBarsTint = new SystemBarsTint\n      val metrics        = contextWrapper.bestAvailable.getResources.getDisplayMetrics\n      val widthContent   = metrics.widthPixels\n      val heightContent =\n        metrics.heightPixels -\n          systemBarsTint.getNavigationBarHeight -\n          systemBarsTint.getStatusBarHeight -\n          resGetDimensionPixelSize(R.dimen.size_icon_app_drawer) -\n          resGetDimensionPixelSize(R.dimen.height_search_box)\n      dimensionToCell(\n        widthContent = widthContent,\n        heightContent = heightContent,\n        minWidth = widget.minWidth,\n        minHeight = widget.minHeight)\n    }\n\n  }\n\n  implicit class WidgetOp(widget: Widget) {\n\n    def convert(newCellX: Int, newCellY: Int): Widget = {\n      val spanX = widget.area.spanX\n      val spanY = widget.area.spanY\n\n      val startX = newCellX - (spanX / 2) match {\n        case sx if sx < 0 => 0\n        case sx if sx + spanX >= WidgetsOps.columns =>\n          WidgetsOps.columns - spanX\n        case sx => sx\n      }\n      val startY = newCellY - (spanY / 2) match {\n        case sy if sy < 0                        => 0\n        case sy if sy + spanY >= WidgetsOps.rows => WidgetsOps.rows - spanY\n        case sy                                  => sy\n      }\n      widget.copy(area = widget.area.copy(startX = startX, startY = startY))\n    }\n\n    def getMovement(to: WidgetArea): WidgetMovement = {\n      val current = widget.area\n      if (current.startX < to.startX) {\n        LeftMovement\n      } else if (current.startX > to.startX) {\n        RightMovement\n      } else if (current.startY < to.startY) {\n        DownMovement\n      } else {\n        UpMovement\n      }\n    }\n\n    def hasSpaceAfterMovement(\n        movedArea: WidgetArea,\n        otherAreas: Seq[WidgetArea],\n        movement: WidgetMovement): Boolean =\n      if (widget.area.intersect(movedArea)) {\n        searchSpaceForMoveWidget(steps(movement), movedArea +: otherAreas).isDefined\n      } else {\n        true\n      }\n\n    def moveToBetterPlace(\n        movedArea: WidgetArea,\n        otherAreas: Seq[WidgetArea],\n        movement: WidgetMovement): Option[Widget] =\n      if (widget.area.intersect(movedArea)) {\n        searchSpaceForMoveWidget(steps(movement), movedArea +: otherAreas) match {\n          case Some(area) => Option(widget.copy(area = area))\n          case _          => None\n        }\n      } else {\n        None\n      }\n\n    lazy val limits = Option((WidgetsOps.rows, WidgetsOps.columns))\n\n    @scala.annotation.tailrec\n    final def searchSpaceForMoveWidget(\n        movements: List[(Int, Int)],\n        areas: Seq[WidgetArea]): Option[WidgetArea] =\n      movements match {\n        case Nil => None\n        case head :: tail =>\n          val (displaceX, displaceY) = head\n          val newPosition = widget.area\n            .copy(startX = widget.area.startX + displaceX, startY = widget.area.startY + displaceY)\n          if (outOfTheLimit(newPosition)) {\n            None\n          } else {\n            val widgetsIntersected =\n              areas.filter(a => newPosition.intersect(a, limits))\n            widgetsIntersected match {\n              case Nil => Option(newPosition)\n              case _   => searchSpaceForMoveWidget(tail, areas)\n            }\n          }\n      }\n\n    private[this] def outOfTheLimit(area: WidgetArea): Boolean =\n      area.spanX <= 0 ||\n        area.spanY <= 0 ||\n        area.startX + area.spanX > WidgetsOps.columns ||\n        area.startY + area.spanY > WidgetsOps.rows\n\n    private[this] def steps(movement: WidgetMovement): List[(Int, Int)] =\n      (movement match {\n        case DownMovement => 1 to widget.area.startY map (p => (0, -p))\n        case UpMovement =>\n          1 until (WidgetsOps.rows - widget.area.startY) map (p => (0, p))\n        case RightMovement =>\n          1 until (WidgetsOps.columns - widget.area.startX) map (p => (p, 0))\n        case LeftMovement => 1 to widget.area.startX map (p => (-p, 0))\n      }).toList\n\n  }\n\n  case class Cell(spanX: Int, spanY: Int, widthCell: Int, heightCell: Int) {\n\n    def getSize: (Int, Int) = (spanX * widthCell, spanY * heightCell)\n\n    def getSize(sX: Int, sY: Int): (Int, Int) =\n      (sX * widthCell, sY * heightCell)\n\n  }\n\n  sealed trait WidgetMovement\n\n  case object UpMovement    extends WidgetMovement\n  case object DownMovement  extends WidgetMovement\n  case object LeftMovement  extends WidgetMovement\n  case object RightMovement extends WidgetMovement\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/states/MomentState.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.states\n\nimport android.content.Context\nimport cards.nine.commons.javaNull\nimport cards.nine.models.types.NineCardsMoment\nimport macroid.ContextWrapper\nimport org.joda.time.DateTime\n\nclass MomentState(implicit contextWrapper: ContextWrapper) {\n\n  private[this] val name = \"persist-moment-preferences\"\n\n  private[this] val timeMomentChangedKey = \"time-moment-changed\"\n\n  private[this] val momentPersistKey = \"moment-persist\"\n\n  private[this] lazy val persistMomentPreferences =\n    contextWrapper.bestAvailable.getSharedPreferences(name, Context.MODE_PRIVATE)\n\n  def persist(momentType: NineCardsMoment): Unit =\n    persistMomentPreferences.edit\n      .putLong(timeMomentChangedKey, new DateTime().getMillis)\n      .putString(momentPersistKey, momentType.name)\n      .apply()\n\n  def clean(): Unit =\n    persistMomentPreferences.edit().remove(timeMomentChangedKey).remove(momentPersistKey).apply()\n\n  def nonPersist: Boolean = {\n    val defaultDate = new DateTime().minusDays(1)\n    val timeChanged = new DateTime(\n      persistMomentPreferences.getLong(timeMomentChangedKey, defaultDate.getMillis))\n    timeChanged.plusHours(1).isBeforeNow\n  }\n\n  def getPersistMoment: Option[NineCardsMoment] =\n    if (nonPersist) {\n      None\n    } else {\n      Option(persistMomentPreferences.getString(momentPersistKey, javaNull)) map (m =>\n                                                                                    NineCardsMoment(\n                                                                                      m))\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/styles/CollectionCardsStyles.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.styles\n\nimport android.graphics.drawable.Drawable\nimport android.support.v7.widget.CardView\nimport android.widget.{Button, TextView}\nimport cards.nine.app.ui.commons.ops.DrawableOps._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.models._\nimport cards.nine.models.types.theme._\nimport macroid.extras.CardViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport macroid.{ContextWrapper, Tweak}\n\ntrait CollectionCardsStyles extends CommonStyles {\n\n  def cardRootStyle(implicit context: ContextWrapper, theme: NineCardsTheme): Tweak[CardView] =\n    cvCardBackgroundColor(theme.get(CardBackgroundColor))\n\n  def textStyle(implicit context: ContextWrapper, theme: NineCardsTheme): Tweak[TextView] =\n    tvColor(theme.get(CardTextColor))\n\n  def buttonStyle(implicit context: ContextWrapper, theme: NineCardsTheme): Tweak[Button] =\n    tvColor(theme.get(DrawerTextColor).alpha(subtitleAlpha)) + vBackground(createBackground)\n\n  def leftDrawableTextStyle(\n      resourceId: Int)(implicit context: ContextWrapper, theme: NineCardsTheme): Tweak[TextView] =\n    tvColor(theme.get(CardTextColor)) + tvCompoundDrawablesWithIntrinsicBounds(\n      left = Some(tintDrawable(resourceId)))\n\n  def tintDrawable(\n      resourceId: Int)(implicit context: ContextWrapper, theme: NineCardsTheme): Drawable =\n    resGetDrawable(resourceId).colorize(theme.get(DrawerIconColor))\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/commons/styles/CommonStyles.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.commons.styles\n\nimport android.annotation.SuppressLint\nimport android.content.res.ColorStateList\nimport android.graphics.Color\nimport android.graphics.drawable.{ColorDrawable, Drawable, RippleDrawable, StateListDrawable}\nimport android.widget.TextView\nimport cards.nine.app.ui.components.widgets.TintableImageView\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.commons._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.{CardBackgroundPressedColor, DrawerTextColor}\nimport macroid.extras.DeviceVersion.Lollipop\nimport macroid.extras.TextViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.{ContextWrapper, Tweak}\n\ntrait CommonStyles {\n\n  val titleAlpha = 0.87f\n\n  val subtitleAlpha = 0.54f\n\n  val alphaDefault = .1f\n\n  def iconMomentStyle(\n      implicit context: ContextWrapper,\n      theme: NineCardsTheme): Tweak[TintableImageView] =\n    tivColor(theme.get(DrawerTextColor))\n\n  def titleTextStyle(implicit context: ContextWrapper, theme: NineCardsTheme): Tweak[TextView] =\n    tvColor(theme.get(DrawerTextColor).alpha(titleAlpha))\n\n  def subtitleTextStyle(implicit context: ContextWrapper, theme: NineCardsTheme): Tweak[TextView] =\n    tvColor(theme.get(DrawerTextColor).alpha(subtitleAlpha))\n\n  def createBackground(implicit context: ContextWrapper, theme: NineCardsTheme): Drawable = {\n\n    @SuppressLint(Array(\"NewApi\"))\n    def createRippleDrawable(color: Int) =\n      new RippleDrawable(\n        new ColorStateList(Array(Array()), Array(color)),\n        javaNull,\n        new ColorDrawable(Color.BLACK.alpha(alphaDefault)))\n\n    @SuppressLint(Array(\"NewApi\"))\n    def createStateListDrawable(color: Int) = {\n      val states = new StateListDrawable()\n      states.addState(\n        Array[Int](android.R.attr.state_pressed),\n        new ColorDrawable(color.alpha(alphaDefault)))\n      states.addState(Array.emptyIntArray, new ColorDrawable(Color.TRANSPARENT))\n      states\n    }\n\n    val color = theme.get(CardBackgroundPressedColor)\n    Lollipop ifSupportedThen createRippleDrawable(color) getOrElse createStateListDrawable(color)\n  }\n\n  protected def getStarDrawable(value: Double): Int = value match {\n    case v if v < 1.1 => R.drawable.recommendations_starts_01\n    case v if v < 1.6 => R.drawable.recommendations_starts_01_5\n    case v if v < 2.1 => R.drawable.recommendations_starts_02\n    case v if v < 2.6 => R.drawable.recommendations_starts_02_5\n    case v if v < 3.1 => R.drawable.recommendations_starts_03\n    case v if v < 3.6 => R.drawable.recommendations_starts_03_5\n    case v if v < 4.1 => R.drawable.recommendations_starts_04\n    case v if v < 4.6 => R.drawable.recommendations_starts_04_5\n    case _            => R.drawable.recommendations_starts_05\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/adapters/ThemeArrayAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.adapters\n\nimport android.text.TextUtils.TruncateAt\nimport android.view.{Gravity, View, ViewGroup}\nimport android.widget.{ArrayAdapter, FrameLayout, TextView}\nimport cards.nine.app.ui.commons.ops.DrawableOps._\nimport cards.nine.app.ui.preferences.commons.FontSize\nimport cards.nine.commons.javaNull\nimport macroid.extras.UIActionsExtras._\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.{DrawerBackgroundColor, DrawerIconColor, DrawerTextColor}\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\n\nclass ThemeArrayAdapter(icons: Seq[Int], values: Seq[String])(\n    implicit contextWrapper: ContextWrapper,\n    theme: NineCardsTheme)\n    extends ArrayAdapter[String](contextWrapper.bestAvailable, 0, values.toArray) {\n\n  val padding = resGetDimensionPixelSize(R.dimen.padding_large)\n\n  override def getCount: Int = values.length\n\n  override def getItemId(position: Int): Long = position\n\n  override def getItem(position: Int): String =\n    values lift position getOrElse javaNull\n\n  override def getView(position: Int, convertView: View, parent: ViewGroup): View =\n    createView(position)\n\n  override def getDropDownView(position: Int, convertView: View, parent: ViewGroup): View =\n    createView(position)\n\n  private[this] def createView(position: Int): FrameLayout = {\n\n    def commonStyle(position: Int) = {\n      val textColor = theme.get(DrawerTextColor)\n      val iconColor = theme.get(DrawerIconColor)\n      val drawableTweak = icons lift position match {\n        case Some(res) =>\n          val drawable = resGetDrawable(res).colorize(iconColor)\n          tvCompoundDrawablesWithIntrinsicBounds(left = Some(drawable)) + tvDrawablePadding(\n            padding)\n        case _ => Tweak.blank\n      }\n      vPaddings(padding) +\n        vSelectableItemBackground +\n        tvGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL) +\n        drawableTweak +\n        tvColor(textColor) +\n        tvLines(1) +\n        tvEllipsize(TruncateAt.END) +\n        tvSizeResource(FontSize.getSizeResource) +\n        tvText(values.lift(position) getOrElse \"\")\n    }\n\n    val backgroundColor = theme.get(DrawerBackgroundColor)\n    (l[FrameLayout](\n      w[TextView] <~ commonStyle(position)\n    ) <~ vBackgroundColor(backgroundColor)).get\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/commons/PaddingItemDecoration.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.commons\n\nimport android.graphics.Rect\nimport android.support.v7.widget.RecyclerView\nimport android.support.v7.widget.RecyclerView.State\nimport android.view.View\nimport cards.nine.app.ui.preferences.commons.CardPadding\nimport macroid.ContextWrapper\n\nclass PaddingItemDecoration(implicit contextWrapper: ContextWrapper)\n    extends RecyclerView.ItemDecoration {\n\n  val padding = CardPadding.getPadding\n\n  override def getItemOffsets(\n      outRect: Rect,\n      view: View,\n      parent: RecyclerView,\n      state: State): Unit = {\n    outRect.top = padding\n    outRect.bottom = padding\n    outRect.left = padding\n    outRect.right = padding\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/commons/ReorderItemTouchHelperCallback.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.commons\n\nimport android.support.v7.widget.RecyclerView\nimport android.support.v7.widget.RecyclerView.ViewHolder\nimport android.support.v7.widget.helper.ItemTouchHelper._\n\nclass ReorderItemTouchHelperCallback(onChanged: (ActionStateReorder, Int) => Unit)\n    extends Callback {\n\n  var statuses = ReorderStatuses()\n\n  override def isLongPressDragEnabled: Boolean = true\n\n  override def onSelectedChanged(viewHolder: ViewHolder, actionState: Int): Unit = {\n    val action = ActionStateReorder(actionState)\n    action match {\n      case ActionStateReordering =>\n        statuses =\n          statuses.copy(from = viewHolder.getAdapterPosition, to = viewHolder.getAdapterPosition)\n        onChanged(action, viewHolder.getAdapterPosition)\n      case ActionStateIdle =>\n        onChanged(action, statuses.to)\n    }\n    super.onSelectedChanged(viewHolder, actionState)\n  }\n\n  override def getMovementFlags(recyclerView: RecyclerView, viewHolder: ViewHolder): Int = {\n    val dragFlags = UP | DOWN | LEFT | RIGHT\n    Callback.makeMovementFlags(dragFlags, 0)\n  }\n\n  override def onMove(\n      recyclerView: RecyclerView,\n      viewHolder: ViewHolder,\n      target: ViewHolder): Boolean = {\n    statuses = statuses.copy(from = viewHolder.getAdapterPosition, to = target.getAdapterPosition)\n    Option(recyclerView.getAdapter) match {\n      case Some(listener: ReorderItemTouchListener) =>\n        listener.onItemMove(statuses.from, statuses.to)\n      case _ =>\n    }\n    true\n  }\n\n  override def onSwiped(viewHolder: ViewHolder, i: Int): Unit = {}\n\n}\n\ntrait ReorderItemTouchListener {\n  def onItemMove(from: Int, to: Int): Unit\n}\n\ncase class ReorderStatuses(from: Int = 0, to: Int = 0)\n\ntrait ActionStateReorder\n\ncase object ActionStateReordering extends ActionStateReorder\n\ncase object ActionStateIdle extends ActionStateReorder\n\nobject ActionStateReorder {\n  def apply(action: Int): ActionStateReorder = action match {\n    case ACTION_STATE_DRAG => ActionStateReordering\n    case _                 => ActionStateIdle\n  }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/commons/SelectedItemDecoration.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.commons\n\nimport android.graphics.Canvas\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.RectShape\nimport android.support.v7.widget.RecyclerView\nimport android.support.v7.widget.RecyclerView.{ItemDecoration, State}\nimport android.view.View\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.layouts.FastScrollerView\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.{CardTextColor, SearchBackgroundColor}\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nclass SelectedItemDecoration(implicit contextWrapper: ContextWrapper, theme: NineCardsTheme)\n    extends ItemDecoration {\n\n  val size = resGetDimensionPixelSize(R.dimen.padding_xlarge)\n\n  val stroke = resGetDimensionPixelSize(R.dimen.stroke_thin)\n\n  val line = {\n    val d = new ShapeDrawable(new RectShape)\n    d.getPaint.setColor(theme.get(CardTextColor))\n    d\n  }\n\n  val divider = {\n    val d = new ShapeDrawable(new RectShape)\n    d.getPaint.setColor(theme.get(SearchBackgroundColor).alpha(.6f))\n    d\n  }\n\n  override def onDrawOver(c: Canvas, parent: RecyclerView, state: State): Unit = {\n    super.onDraw(c, parent, state)\n    for {\n      recyclerView <- Option(parent)\n      pos          <- recyclerView.getField[Int](FastScrollerView.fastScrollerPositionKey)\n      count        <- recyclerView.getField[Int](FastScrollerView.fastScrollerCountKey)\n    } yield {\n      val showLine = recyclerView.getField[Boolean](SelectedItemDecoration.showLine) getOrElse false\n      (0 to recyclerView.getChildCount flatMap (i => Option(recyclerView.getChildAt(i)))) foreach {\n        view =>\n          val viewPosition = parent.getChildAdapterPosition(view)\n          draw(c, view, viewPosition, pos, count, showLine)\n      }\n    }\n  }\n\n  private[this] def draw(\n      c: Canvas,\n      child: View,\n      viewPosition: Int,\n      pos: Int,\n      count: Int,\n      showLine: Boolean) = {\n    if (viewPosition < pos || viewPosition >= (pos + count)) {\n      divider.setBounds(child.getLeft, child.getTop, child.getRight, child.getBottom)\n      divider.draw(c)\n    } else if (showLine) {\n      val left   = child.getLeft + (child.getWidth / 2) - (size / 2)\n      val right  = left + size\n      val top    = child.getTop + child.getHeight - child.getPaddingBottom\n      val bottom = top + stroke\n      line.setBounds(left, top, right, bottom)\n      line.draw(c)\n    }\n  }\n\n}\n\nobject SelectedItemDecoration {\n  val showLine = \"show_line\"\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/commons/SwipeController.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.commons\n\nimport android.content.Context\nimport android.support.v4.view.MotionEventCompat\nimport android.view.MotionEvent._\nimport android.view.{MotionEvent, VelocityTracker, ViewConfiguration}\nimport cards.nine.commons.javaNull\n\ntrait SwipeController {\n\n  def getContext: Context\n\n  val computeUnitsTracker = 1000\n\n  lazy val (maximumVelocity, minimumVelocity) = {\n    val configuration: ViewConfiguration = ViewConfiguration.get(getContext)\n    (configuration.getScaledMaximumFlingVelocity, configuration.getScaledMinimumFlingVelocity)\n  }\n\n  // We need to use \"null\" instead of Option because we must assign \"null\" when we recycle\n  // the velocityTracker for avoid IllegalStateException in Android SDK\n  private[this] var velocityTracker: VelocityTracker = javaNull\n\n  def updateSwipe(event: MotionEvent): Unit = {\n    val action = MotionEventCompat.getActionMasked(event)\n    action match {\n      case ACTION_DOWN =>\n        if (velocityTracker == javaNull) {\n          velocityTracker = VelocityTracker.obtain()\n        }\n        velocityTracker.addMovement(event)\n      case ACTION_MOVE =>\n        velocityTracker.addMovement(event)\n      case _ =>\n    }\n  }\n\n  def currentSwiping: Swiping = {\n    velocityTracker.computeCurrentVelocity(computeUnitsTracker, maximumVelocity)\n    velocityTracker.getXVelocity match {\n      case v if v > minimumVelocity  => SwipeLeft(v)\n      case v if v < -minimumVelocity => SwipeRight(v)\n      case _                         => NoSwipe()\n    }\n  }\n\n  def recycleSwipe(): Unit = {\n    velocityTracker.recycle()\n    velocityTracker = javaNull\n  }\n\n}\n\nsealed trait Swiping {\n  def getVelocity: Float = 0\n}\n\ncase class SwipeLeft(velocity: Float) extends Swiping {\n  override def getVelocity: Float = velocity\n}\n\ncase class SwipeRight(velocity: Float) extends Swiping {\n  override def getVelocity: Float = velocity\n}\n\ncase class NoSwipe() extends Swiping\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/commons/TranslationAnimator.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.commons\n\nimport android.animation.{Animator, AnimatorListenerAdapter, ObjectAnimator, ValueAnimator}\nimport android.view.View\nimport android.view.animation.DecelerateInterpolator\nimport cards.nine.app.ui.preferences.commons.SpeedAnimations\nimport macroid.{ContextWrapper, Snail, Ui}\n\nimport scala.concurrent.Promise\n\nclass TranslationAnimator(\n    translation: Translation = NoTranslation,\n    update: (Float) => Ui[_],\n    end: () => Ui[_] = () => Ui.nop)(implicit context: ContextWrapper) {\n\n  val duration = SpeedAnimations.getDuration\n\n  private[this] val animator: ValueAnimator = translation match {\n    case NoTranslation => new ValueAnimator\n    case _ =>\n      val objectAnimator = new ObjectAnimator\n      objectAnimator.setPropertyName(translation.name)\n      objectAnimator\n  }\n  animator.setInterpolator(new DecelerateInterpolator())\n  animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n    override def onAnimationUpdate(value: ValueAnimator) =\n      update(value.getAnimatedValue.asInstanceOf[Float]).run\n  })\n\n  def move(\n      from: Float,\n      to: Float,\n      duration: Int = duration,\n      attachTarget: Boolean = false): Snail[View] = Snail[View] { view =>\n    val promise = Promise[Unit]()\n    animator.removeAllListeners()\n    animator.addListener(new AnimatorListenerAdapter() {\n      override def onAnimationEnd(animation: Animator) = {\n        super.onAnimationEnd(animation)\n        promise.trySuccess(())\n        end().run\n      }\n    })\n    if (attachTarget) animator.setTarget(view)\n    animator.setFloatValues(from, to)\n    animator.setDuration(duration)\n    animator.start()\n    promise.future\n  }\n\n  def cancel(): Unit = animator.cancel()\n\n  def isRunning: Boolean = animator.isRunning\n\n}\n\nsealed trait Translation {\n  val name: String\n}\n\ncase object TranslationX extends Translation {\n  override val name: String = \"translationX\"\n}\n\ncase object TranslationY extends Translation {\n  override val name: String = \"translationY\"\n}\n\ncase object NoTranslation extends Translation {\n  override val name: String = \"\"\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/commons/ViewState.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.commons\n\nsealed trait ViewState\n\ncase object Stopped extends ViewState\n\ncase object Scrolling extends ViewState\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/dialogs/AlertDialogFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.dialogs\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.content.DialogInterface.OnClickListener\nimport android.os.Bundle\nimport android.support.v4.app.DialogFragment\nimport android.support.v7.app.AlertDialog\n\nclass AlertDialogFragment(\n    message: Int,\n    okMessage: Int = android.R.string.ok,\n    cancelMessage: Int = android.R.string.cancel,\n    positiveAction: () => Unit = () => (),\n    negativeAction: () => Unit = () => (),\n    showCancelButton: Boolean = true)\n    extends DialogFragment {\n\n  override def onCreateDialog(savedInstanceState: Bundle): Dialog = {\n\n    val alert = new AlertDialog.Builder(getActivity)\n      .setMessage(message)\n      .setPositiveButton(okMessage, new OnClickListener {\n        override def onClick(dialog: DialogInterface, which: Int): Unit = {\n          positiveAction()\n          dismiss()\n        }\n      })\n    if (showCancelButton) {\n      alert.setNegativeButton(cancelMessage, new OnClickListener {\n        override def onClick(dialog: DialogInterface, which: Int): Unit = {\n          negativeAction()\n          dismiss()\n        }\n      })\n    }\n    alert.create()\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/dialogs/BluetoothDialogFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.dialogs\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport android.support.v4.app.DialogFragment\nimport android.support.v7.app.AlertDialog\nimport android.view.{Gravity, LayoutInflater}\nimport android.widget.{LinearLayout, ScrollView, TextView}\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.ops.DrawableOps._\nimport cards.nine.models._\nimport cards.nine.models.types.theme.{DrawerBackgroundColor, DrawerIconColor, DrawerTextColor}\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\n\ncase class BluetoothDialogFragment(devices: Seq[String], onSelected: (String) => Unit)(\n    implicit contextWrapper: ContextWrapper,\n    theme: NineCardsTheme)\n    extends DialogFragment\n    with AppNineCardsIntentConversions {\n\n  override def onCreateDialog(savedInstanceState: Bundle): Dialog = {\n    val rootView    = new ScrollView(getActivity)\n    val contentView = new LinearLayout(getActivity)\n    contentView.setOrientation(LinearLayout.VERTICAL)\n\n    val views = if (devices.isEmpty) {\n      Seq(\n        (w[TextView] <~\n          vMatchWidth <~\n          tvGravity(Gravity.CENTER) <~\n          vPaddings(resGetDimensionPixelSize(R.dimen.padding_large)) <~\n          tvText(R.string.bluetoothDisconnected) <~\n          tvColor(theme.get(DrawerTextColor)) <~\n          tvSizeResource(R.dimen.text_default)).get)\n    } else {\n      devices map (new ItemView(_))\n    }\n\n    ((rootView <~ vBackgroundColor(theme.get(DrawerBackgroundColor)) <~ vgAddView(contentView)) ~\n      (contentView <~ vgAddViews(views))).run\n\n    new AlertDialog.Builder(getActivity).setView(rootView).create()\n  }\n\n  class ItemView(devices: String)\n      extends LinearLayout(contextWrapper.bestAvailable)\n      with TypedFindView {\n\n    LayoutInflater.from(getActivity).inflate(R.layout.icon_info_item_dialog, this)\n\n    lazy val text = Option(findView(TR.icon_dialog_name))\n    lazy val icon = Option(findView(TR.icon_dialog_select))\n\n    val colorizeDrawable =\n      resGetDrawable(R.drawable.icon_edit_moment_bluetooth).colorize(theme.get(DrawerIconColor))\n\n    ((text <~\n      tvColor(theme.get(DrawerTextColor)) <~\n      tvText(devices) <~\n      tvCompoundDrawablesWithIntrinsicBounds(left = Some(colorizeDrawable))) ~\n      (this <~ On.click {\n        Ui {\n          onSelected(devices)\n          dismiss()\n        }\n      })).run\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/dialogs/CollectionDialog.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.dialogs\n\nimport android.app.Dialog\nimport android.support.design.widget.BottomSheetDialogFragment\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport android.widget.LinearLayout\nimport cards.nine.app.ui.commons.ops.CollectionOps._\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.commons._\nimport cards.nine.models.types.theme.{DrawerBackgroundColor, DrawerIconColor, DrawerTextColor}\nimport cards.nine.models.{Collection, NineCardsTheme}\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\n\nclass CollectionDialog(\n    moments: Seq[Collection],\n    onCollection: (Int) => Unit,\n    onDismissDialog: () => Unit)(implicit contextWrapper: ContextWrapper, theme: NineCardsTheme)\n    extends BottomSheetDialogFragment\n    with TypedFindView { dialog =>\n\n  lazy val selectCollectionList = findView(TR.select_collection_list)\n\n  var rootView: Option[ViewGroup] = None\n\n  override protected def findViewById(id: Int): View =\n    rootView.map(_.findViewById(id)).orNull\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n    val baseView = LayoutInflater\n      .from(getActivity)\n      .inflate(R.layout.select_collection_dialog, javaNull, false)\n      .asInstanceOf[ViewGroup]\n    rootView = Option(baseView)\n    val views = moments map (moment => new CollectionItem(moment))\n    (selectCollectionList <~\n      vBackgroundColor(theme.get(DrawerBackgroundColor)) <~\n      vgAddViews(views)).run\n    dialog.setContentView(baseView)\n  }\n\n  class CollectionItem(collection: Collection)\n      extends LinearLayout(contextWrapper.getOriginal)\n      with TypedFindView {\n\n    LayoutInflater.from(getContext).inflate(TR.layout.select_collection_item, this)\n\n    val icon = findView(TR.select_collection_item_icon)\n\n    val text = findView(TR.select_collection_item_text)\n\n    ((this <~ On.click(Ui {\n      onCollection(collection.id)\n      dialog.dismiss()\n    })) ~\n      (icon <~ ivSrc(collection.getIconDetail) <~ tivDefaultColor(theme.get(DrawerIconColor))) ~\n      (text <~ tvText(collection.name) <~ tvColor(theme.get(DrawerTextColor)))).run\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/dialogs/MomentDialog.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.dialogs\n\nimport android.app.Dialog\nimport android.support.design.widget.BottomSheetDialogFragment\nimport android.view.ViewGroup.LayoutParams._\nimport android.view.{Gravity, LayoutInflater, View, ViewGroup}\nimport android.widget.FrameLayout.LayoutParams\nimport android.widget.{LinearLayout, TextView}\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons.ops.NineCardsMomentOps._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.commons.states.MomentState\nimport cards.nine.app.ui.components.widgets.TintableImageView\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.app.ui.launcher.jobs.{LauncherJobs, NavigationJobs}\nimport cards.nine.commons._\nimport cards.nine.models.types.NineCardsMoment\nimport cards.nine.models.types.theme.{\n  DrawerBackgroundColor,\n  DrawerIconColor,\n  DrawerTextColor,\n  PrimaryColor\n}\nimport cards.nine.models.{Moment, NineCardsTheme}\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.FrameLayoutTweaks._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.LinearLayoutTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\n\nimport scala.concurrent.ExecutionContext.Implicits.global\n\nclass MomentDialog(moments: Seq[Moment])(\n    implicit contextWrapper: ContextWrapper,\n    launcherJobs: LauncherJobs,\n    navigationJobs: NavigationJobs,\n    theme: NineCardsTheme)\n    extends BottomSheetDialogFragment\n    with TypedFindView { dialog =>\n\n  lazy val momentState = new MomentState\n\n  lazy val selectMomentList = findView(TR.select_moment_list)\n\n  val hideableKey = \"hideable-key\"\n\n  var rootView: Option[ViewGroup] = None\n\n  val paddingDefault = resGetDimensionPixelSize(R.dimen.padding_default)\n\n  val paddingLarge = resGetDimensionPixelSize(R.dimen.padding_large)\n\n  override protected def findViewById(id: Int): View =\n    rootView.map(_.findViewById(id)).orNull\n\n  override def getTheme: Int = R.style.AppThemeDialog\n\n  override def setupDialog(dialog: Dialog, style: Int): Unit = {\n    super.setupDialog(dialog, style)\n    val baseView = LayoutInflater\n      .from(getActivity)\n      .inflate(R.layout.select_moment_dialog, javaNull, false)\n      .asInstanceOf[ViewGroup]\n    rootView = Option(baseView)\n    val momentItems  = moments map (moment => new MomentItem(moment.momentType, moment.id))\n    val paramsHeader = new LayoutParams(MATCH_PARENT, WRAP_CONTENT)\n    (selectMomentList <~\n      vBackgroundColor(theme.get(DrawerBackgroundColor)) <~\n      vgAddViews(momentItems) <~\n      vgAddViewByIndexParams(createHeader(), 0, paramsHeader)).run\n\n    dialog.setContentView(baseView)\n  }\n\n  def createHeader(): LinearLayout = {\n    def swapIcons = rootView <~ Transformer {\n      case image: TintableImageView\n          if image.isType(hideableKey) && image.getVisibility == View.VISIBLE =>\n        image <~~ applyFadeOut() <~ vGone\n      case image: TintableImageView\n          if image.isType(hideableKey) && image.getVisibility == View.GONE =>\n        image <~ applyFadeIn()\n    }\n\n    (l[LinearLayout](\n      w[TextView] <~\n        llMatchWeightHorizontal <~\n        tvColor(theme.get(DrawerTextColor)) <~\n        tvGravity(Gravity.CENTER_VERTICAL) <~\n        vPadding(paddingLeft = paddingDefault) <~\n        tvBoldLight <~\n        tvText(R.string.select_moment) <~\n        tvSizeResource(R.dimen.text_xlarge),\n      w[TintableImageView] <~\n        vWrapContent <~\n        vSelectableItemBackground <~\n        vPaddings(paddingLarge) <~\n        ivSrc(R.drawable.icon_action_bar_options) <~\n        flLayoutGravity(Gravity.RIGHT) <~\n        tivColor(theme.get(DrawerIconColor)) <~\n        On.click(swapIcons)) <~\n      vPadding(paddingLeft = paddingDefault, paddingRight = paddingDefault)).get\n  }\n\n  class MomentItem(moment: NineCardsMoment, id: Int)\n      extends LinearLayout(contextWrapper.getOriginal)\n      with TypedFindView {\n\n    LayoutInflater.from(getContext).inflate(TR.layout.select_moment_item, this)\n\n    val icon = findView(TR.select_moment_item_icon)\n\n    val text = findView(TR.select_moment_item_text)\n\n    val pin = findView(TR.select_moment_item_pin)\n\n    val edit = findView(TR.select_moment_item_edit)\n\n    val delete = findView(TR.select_moment_item_delete)\n\n    val line = findView(TR.select_moment_item_line)\n\n    val momentPersisted = momentState.getPersistMoment.contains(moment)\n\n    val colorPined =\n      if (momentPersisted) theme.get(PrimaryColor)\n      else theme.get(DrawerTextColor)\n\n    val colorTheme = theme.get(DrawerTextColor)\n\n    val pinActionTweak = if (momentPersisted) {\n      tivColor(colorPined) +\n        On.click(Ui {\n          launcherJobs.cleanPersistedMoment().resolveAsync()\n          dialog.dismiss()\n        }) +\n        vVisible\n    } else {\n      vGone\n    }\n\n    ((this <~ On.click(Ui {\n      launcherJobs.changeMoment(id).resolveAsync()\n      dialog.dismiss()\n    })) ~\n      (line <~ vBackgroundColor(theme.getLineColor)) ~\n      (icon <~ ivSrc(moment.getIconCollectionDetail) <~ tivDefaultColor(colorPined)) ~\n      (text <~ tvText(moment.getName) <~ tvColor(colorPined)) ~\n      (pin <~ pinActionTweak) ~\n      (edit <~\n        vSetType(hideableKey) <~\n        vGone <~\n        tivColor(colorTheme) <~\n        On.click(Ui {\n          navigationJobs.launchEditMoment(moment.name).resolveAsync()\n          dialog.dismiss()\n        })) ~\n      (delete <~\n        vSetType(hideableKey) <~\n        vGone <~\n        tivColor(colorTheme) <~\n        On.click(Ui {\n          launcherJobs.removeMomentDialog(moment, id).resolveAsync()\n          dialog.dismiss()\n        }))).run\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/dialogs/WifiDialogFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.dialogs\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport android.support.v4.app.DialogFragment\nimport android.support.v7.app.AlertDialog\nimport android.view.{Gravity, LayoutInflater}\nimport android.widget.{LinearLayout, ScrollView, TextView}\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.ops.DrawableOps._\nimport cards.nine.models._\nimport cards.nine.models.types.theme.{\n  DrawerBackgroundColor,\n  DrawerIconColor,\n  DrawerTextColor,\n  PrimaryColor\n}\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\ncase class WifiDialogFragment(wifis: Seq[String], onSelected: (String) => Unit)(\n    implicit contextWrapper: ContextWrapper,\n    theme: NineCardsTheme)\n    extends DialogFragment\n    with AppNineCardsIntentConversions {\n\n  override def onCreateDialog(savedInstanceState: Bundle): Dialog = {\n    val rootView    = new ScrollView(getActivity)\n    val contentView = new LinearLayout(getActivity)\n    contentView.setOrientation(LinearLayout.VERTICAL)\n\n    val views = if (wifis.isEmpty) {\n      Seq(\n        (w[TextView] <~\n          vMatchWidth <~\n          tvGravity(Gravity.CENTER) <~\n          vPaddings(resGetDimensionPixelSize(R.dimen.padding_large)) <~\n          tvText(R.string.wifiDisconnected) <~\n          tvColor(theme.get(DrawerTextColor)) <~\n          tvSizeResource(R.dimen.text_default)).get)\n    } else {\n      wifis map (new ItemView(_))\n    }\n\n    ((rootView <~ vBackgroundColor(theme.get(DrawerBackgroundColor)) <~ vgAddView(contentView)) ~\n      (contentView <~ vgAddViews(views))).run\n\n    new AlertDialog.Builder(getActivity).setView(rootView).create()\n  }\n\n  class ItemView(wifi: String)\n      extends LinearLayout(contextWrapper.bestAvailable)\n      with TypedFindView {\n\n    LayoutInflater.from(getActivity).inflate(R.layout.icon_info_item_dialog, this)\n\n    lazy val text = Option(findView(TR.icon_dialog_name))\n    lazy val icon = Option(findView(TR.icon_dialog_select))\n\n    val primaryColor = theme.get(PrimaryColor)\n\n    val colorizeDrawable =\n      resGetDrawable(R.drawable.icon_edit_moment_wifi).colorize(theme.get(DrawerIconColor))\n\n    ((text <~\n      tvColor(theme.get(DrawerTextColor)) <~\n      tvText(wifi) <~\n      tvCompoundDrawablesWithIntrinsicBounds(left = Some(colorizeDrawable))) ~\n      (this <~ On.click {\n        Ui {\n          onSelected(wifi)\n          dismiss()\n        }\n      })).run\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/drawables/BackgroundSelectedDrawable.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.drawables\n\nimport android.graphics._\nimport android.graphics.drawable.Drawable\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.{CardBackgroundColor, PrimaryColor}\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nclass BackgroundSelectedDrawable(implicit contextWrapper: ContextWrapper, theme: NineCardsTheme)\n    extends Drawable {\n\n  val stroke = resGetDimensionPixelSize(R.dimen.stroke_thin).toFloat\n\n  val paintStroke: Paint = {\n    val paint = new Paint\n    paint.setAntiAlias(true)\n    paint.setDither(true)\n    paint\n  }\n\n  val paintBack: Paint = {\n    val paint = new Paint\n    paint.setAntiAlias(true)\n    paint.setDither(true)\n    paint\n  }\n\n  selected(s = false)\n\n  def selected(s: Boolean) = {\n    if (s) {\n      paintStroke.setColor(theme.get(PrimaryColor))\n      paintBack.setColor(theme.get(PrimaryColor))\n    } else {\n      paintStroke.setColor(theme.get(PrimaryColor))\n      paintBack.setColor(theme.get(CardBackgroundColor))\n    }\n    invalidateSelf()\n  }\n\n  override def draw(canvas: Canvas): Unit = {\n    val bounds  = getBounds\n    val middleX = bounds.width() / 2\n    val middleY = bounds.height() / 2\n    val radius  = if (middleX < middleY) middleX else middleY\n    canvas.drawCircle(middleX, middleY, radius, paintStroke)\n    canvas.drawCircle(middleX, middleY, radius - stroke, paintBack)\n  }\n\n  override def setColorFilter(cf: ColorFilter): Unit =\n    paintBack.setColorFilter(cf)\n\n  override def setAlpha(alpha: Int): Unit = paintBack.setAlpha(alpha)\n\n  override def getOpacity: Int = PixelFormat.TRANSPARENT\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/drawables/CharDrawable.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.drawables\n\nimport android.graphics._\nimport android.graphics.drawable.Drawable\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nimport scala.annotation.tailrec\n\ncase class CharDrawable(char: String, circle: Boolean = false, background: Option[Int] = None)(\n    implicit contextWrapper: ContextWrapper)\n    extends Drawable {\n\n  val ratioChars = .3f\n\n  val colors = List(\n    resGetColor(R.color.background_default_1),\n    resGetColor(R.color.background_default_2),\n    resGetColor(R.color.background_default_3),\n    resGetColor(R.color.background_default_4),\n    resGetColor(R.color.background_default_5))\n\n  val backgroundColor = background getOrElse colors(positionByChar())\n\n  var parentBounds: Option[Rect] = None\n\n  lazy val backgroundPaint = {\n    val paint = new Paint\n    paint.setColor(backgroundColor)\n    paint\n  }\n\n  val charPaint: Paint = {\n    val paint = new Paint\n    paint.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL))\n    paint.setAntiAlias(true)\n    paint.setColor(Color.WHITE)\n    paint.setTextAlign(Paint.Align.CENTER)\n    paint\n  }\n\n  override def onBoundsChange(b: Rect): Unit = {\n    parentBounds = Option(b)\n    charPaint.setTextSize(determineMaxTextSize(b.width() * .5f))\n    super.onBoundsChange(b)\n  }\n\n  override def draw(canvas: Canvas): Unit = {\n    parentBounds foreach { pb =>\n      if (circle) {\n        canvas.drawCircle(pb.centerX(), pb.centerY(), pb.width() / 2, backgroundPaint)\n      } else {\n        canvas.drawColor(backgroundColor)\n      }\n      val bounds = new Rect\n      charPaint.getTextBounds(char, 0, 1, bounds)\n      val x: Int = pb.centerX()\n      val y: Int = (pb.centerY() - bounds.exactCenterY).toInt\n      canvas.drawText(char.toUpperCase, x, y, charPaint)\n    }\n  }\n\n  override def setColorFilter(cf: ColorFilter): Unit =\n    charPaint.setColorFilter(cf)\n\n  override def setAlpha(alpha: Int): Unit = charPaint.setAlpha(alpha)\n\n  override def getOpacity: Int = PixelFormat.TRANSPARENT\n\n  private[this] def determineMaxTextSize(maxWidth: Float): Int = {\n    val paint: Paint = new Paint\n    @tailrec\n    def calculateSize(size: Int): Int = {\n      paint.setTextSize(size)\n      if (paint.measureText(\"M\") >= maxWidth) {\n        size\n      } else {\n        calculateSize(size + 1)\n      }\n    }\n    (calculateSize(0) / calculateRatioChars).toInt\n  }\n\n  private[this] def calculateRatioChars = 1 + ((char.length - 1) * ratioChars)\n\n  private[this] def positionByChar(): Int = {\n    val abc = \"abcdefghijklmnñopqrstuvwxyz0123456789\"\n    abc.indexOf(char.toLowerCase) match {\n      case i if i < 0 => 0\n      case i          => i % colors.length\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/drawables/DottedDrawable.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.drawables\n\nimport android.graphics._\nimport macroid.extras.ResourcesExtras._\nimport android.graphics.drawable.Drawable\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nclass DottedDrawable(horizontal: Boolean = true)(implicit contextWrapper: ContextWrapper)\n    extends Drawable {\n\n  val paddingDefault = resGetDimensionPixelSize(R.dimen.padding_default).toFloat\n\n  val paint: Paint = {\n    val paint = new Paint\n    paint.setAntiAlias(true)\n    paint.setDither(true)\n    paint.setColor(resGetColor(R.color.stroke_rules_moment))\n    paint.setStrokeWidth(resGetDimensionPixelSize(R.dimen.stroke_thin))\n    paint.setStyle(Paint.Style.STROKE)\n    paint.setPathEffect(new DashPathEffect(Array(paddingDefault, paddingDefault), 0))\n    paint\n  }\n\n  override def draw(canvas: Canvas): Unit = {\n    val bounds = getBounds\n    val path   = new Path()\n    path.moveTo(0, 0)\n    if (horizontal) path.lineTo(bounds.width(), 0)\n    else path.lineTo(0, bounds.height())\n    canvas.drawPath(path, paint)\n  }\n\n  override def setColorFilter(cf: ColorFilter): Unit = paint.setColorFilter(cf)\n\n  override def setAlpha(alpha: Int): Unit = paint.setAlpha(alpha)\n\n  override def getOpacity: Int = PixelFormat.TRANSPARENT\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/drawables/DrawerAnimationBackgroundDrawable.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.drawables\n\nimport android.graphics._\nimport android.graphics.drawable.Drawable\nimport macroid.extras.SnailsUtils\n\nclass DrawerAnimationBackgroundDrawable(backgroundColor: Int, circleColor: Int) extends Drawable {\n\n  private[this] var statuses = BackgroundDrawerAnimationStatuses()\n\n  lazy val circlePaint = {\n    val paint = new Paint\n    paint.setColor(circleColor)\n    paint\n  }\n\n  override def draw(canvas: Canvas): Unit = {\n    val bounds = getBounds\n    val radius =\n      SnailsUtils.calculateRadius(statuses.x, bounds.centerY(), bounds.width(), bounds.height())\n    canvas.drawColor(backgroundColor)\n    canvas.drawCircle(statuses.x, bounds.centerY(), radius * statuses.percentage, circlePaint)\n  }\n\n  override def setColorFilter(cf: ColorFilter): Unit =\n    circlePaint.setColorFilter(cf)\n\n  override def setAlpha(alpha: Int): Unit = circlePaint.setAlpha(alpha)\n\n  override def getOpacity: Int = PixelFormat.TRANSPARENT\n\n  def setData(p: Float, x: Int) = {\n    statuses = statuses.copy(percentage = p, x = x)\n    invalidateSelf()\n  }\n\n}\n\ncase class BackgroundDrawerAnimationStatuses(percentage: Float = 0, x: Int = 0)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/drawables/DrawerBackgroundDrawable.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.drawables\n\nimport android.graphics._\nimport android.graphics.drawable.Drawable\n\nclass DrawerBackgroundDrawable(\n    color: Int = 0,\n    horizontalPadding: Int,\n    verticalPadding: Int,\n    radius: Int)\n    extends Drawable {\n\n  lazy val backgroundPaint = {\n    val paint = new Paint\n    paint.setColor(color)\n    paint\n  }\n\n  override def setColorFilter(cf: ColorFilter): Unit =\n    backgroundPaint.setColorFilter(cf)\n\n  override def setAlpha(alpha: Int): Unit = backgroundPaint.setAlpha(alpha)\n\n  override def getOpacity: Int = PixelFormat.TRANSPARENT\n\n  override def draw(canvas: Canvas): Unit = {\n    val bounds = getBounds\n    canvas.drawRoundRect(\n      new RectF(\n        horizontalPadding,\n        verticalPadding,\n        bounds.width() - horizontalPadding,\n        bounds.height() - verticalPadding),\n      radius,\n      radius,\n      backgroundPaint)\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/drawables/DropBackgroundDrawable.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.drawables\n\nimport android.animation.{Animator, AnimatorListenerAdapter, ValueAnimator}\nimport android.graphics._\nimport android.graphics.drawable.Drawable\nimport android.view.animation.DecelerateInterpolator\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.SnailsUtils\nimport cards.nine.app.ui.preferences.commons.SpeedAnimations\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\n\nimport scala.concurrent.{Future, Promise}\n\nclass DropBackgroundDrawable(implicit contextWrapper: ContextWrapper) extends Drawable {\n\n  private[this] var percentage: Float = 0\n\n  private[this] val duration = SpeedAnimations.getDuration\n\n  lazy val circlePaint = {\n    val paint = new Paint\n    paint.setColor(resGetColor(R.color.collection_workspace_feedback_drop))\n    paint.setAntiAlias(true)\n    paint\n  }\n\n  private[this] val animator: ValueAnimator = {\n    val valueAnimation = new ValueAnimator\n    valueAnimation.setInterpolator(new DecelerateInterpolator())\n    valueAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n      override def onAnimationUpdate(value: ValueAnimator) =\n        update(value.getAnimatedValue.asInstanceOf[Float])\n    })\n    valueAnimation\n  }\n\n  override def setColorFilter(cf: ColorFilter): Unit =\n    circlePaint.setColorFilter(cf)\n\n  override def setAlpha(alpha: Int): Unit = circlePaint.setAlpha(alpha)\n\n  override def getOpacity: Int = PixelFormat.TRANSPARENT\n\n  override def draw(canvas: Canvas): Unit = {\n    val bounds = getBounds\n    val x      = bounds.width() / 2\n    val y      = bounds.height() / 2\n    canvas.drawCircle(x, y, x * percentage, circlePaint)\n  }\n\n  def start(): Ui[Future[Any]] = animation(0f, 1f)\n\n  def end(): Ui[Future[Any]] = {\n    if (animator.isRunning) animator.cancel()\n    animation(percentage, 0f)\n  }\n\n  private[this] def animation(from: Float, to: Float): Ui[Future[Any]] = Ui {\n    val promise = Promise[Unit]()\n    animator.removeAllListeners()\n    animator.addListener(new AnimatorListenerAdapter() {\n      override def onAnimationEnd(animation: Animator): Unit = {\n        super.onAnimationEnd(animation)\n        promise.trySuccess(())\n      }\n    })\n    animator.setFloatValues(from, to)\n    animator.setDuration(duration)\n    animator.start()\n    promise.future\n  }\n\n  private[this] def update(p: Float) = {\n    percentage = p\n    invalidateSelf()\n  }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/drawables/EdgeWorkspaceDrawable.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.drawables\n\nimport android.graphics._\nimport android.graphics.drawable.Drawable\nimport cards.nine.commons.ops.ColorOps._\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nclass EdgeWorkspaceDrawable(left: Boolean)(implicit contextWrapper: ContextWrapper)\n    extends Drawable {\n\n  val color = resGetColor(R.color.collection_workspace_feedback_drop)\n\n  lazy val fillPaint = {\n    val paint = new Paint\n    paint.setColor(color)\n    paint.setAntiAlias(true)\n    paint\n  }\n\n  lazy val strokePaint = {\n    val paint = new Paint\n    paint.setStrokeWidth(resGetDimensionPixelSize(R.dimen.stroke_default))\n    paint.setColor(color.alpha(1f))\n    paint.setAntiAlias(true)\n    paint\n  }\n\n  override def setColorFilter(cf: ColorFilter): Unit =\n    fillPaint.setColorFilter(cf)\n\n  override def setAlpha(alpha: Int): Unit = fillPaint.setAlpha(alpha)\n\n  override def getOpacity: Int = PixelFormat.TRANSPARENT\n\n  override def draw(canvas: Canvas): Unit = {\n    val bounds = getBounds\n    canvas.drawRect(bounds, fillPaint)\n    // top line\n    canvas.drawLine(0, 0, bounds.width(), 0, strokePaint)\n    // bottom line\n    canvas.drawLine(0, bounds.height(), bounds.width(), bounds.height(), strokePaint)\n    // left or right line\n    if (left) {\n      canvas.drawLine(bounds.width(), 0, bounds.width(), bounds.height(), strokePaint)\n    } else {\n      canvas.drawLine(0, 0, 0, bounds.height(), strokePaint)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/drawables/PathMorphDrawable.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.drawables\n\nimport android.animation.ValueAnimator.AnimatorUpdateListener\nimport android.animation.{Animator, AnimatorListenerAdapter, ValueAnimator}\nimport android.graphics.Paint.Style\nimport android.graphics._\nimport android.graphics.drawable.{Animatable, Drawable}\nimport android.support.v4.content.ContextCompat\nimport android.view.animation.DecelerateInterpolator\nimport cards.nine.app.ui.components.drawables.IconTypes._\nimport macroid.ContextWrapper\n\ncase class PathMorphDrawable(\n    defaultIcon: Int = BACK,\n    defaultStroke: Int = 3,\n    defaultColor: Int = Color.WHITE,\n    padding: Int = 0)(implicit context: ContextWrapper)\n    extends Drawable\n    with Animatable\n    with PathMorphDrawableTypes {\n\n  implicit var size: Option[Dim] = None\n\n  lazy val burgerIcon = List(\n    Segment().fromRatios(0.2f, 0.35f, 0.8f, 0.35f),\n    Segment().fromRatios(0.2f, 0.5f, 0.8f, 0.5f),\n    Segment().fromRatios(0.2f, 0.65f, 0.8f, 0.65f))\n\n  lazy val backIcon = List(\n    Segment().fromRatios(0.3f, 0.51f, 0.5f, 0.3f),\n    Segment().fromRatios(0.33f, 0.5f, 0.7f, 0.5f),\n    Segment().fromRatios(0.3f, 0.49f, 0.5f, 0.7f))\n\n  lazy val upIcon = List(\n    Segment().fromRatios(0.49f, 0.3f, 0.7f, 0.5f),\n    Segment().fromRatios(0.5f, 0.33f, 0.5f, 0.7f),\n    Segment().fromRatios(0.51f, 0.3f, 0.3f, 0.5f))\n\n  lazy val downIcon = List(\n    Segment().fromRatios(0.51f, 0.7f, 0.3f, 0.5f),\n    Segment().fromRatios(0.5f, 0.67f, 0.5f, 0.3f),\n    Segment().fromRatios(0.49f, 0.7f, 0.7f, 0.5f))\n\n  lazy val nextIcon = List(\n    Segment().fromRatios(0.7f, 0.49f, 0.5f, 0.7f),\n    Segment().fromRatios(0.67f, 0.5f, 0.3f, 0.5f),\n    Segment().fromRatios(0.7f, 0.51f, 0.5f, 0.3f))\n\n  lazy val checkIcon = List(\n    Segment().fromRatios(0.2f, 0.6f, 0.4f, 0.8f),\n    Segment().fromRatios(0.4f, 0.8f, 0.8f, 0.2f))\n\n  lazy val addIcon = List(\n    Segment().fromRatios(0.5f, 0.2f, 0.5f, 0.8f),\n    Segment().fromRatios(0.2f, 0.5f, 0.8f, 0.5f))\n\n  lazy val closeIcon = List(\n    Segment().fromRatios(0.662f, 0.338f, 0.338f, 0.662f),\n    Segment().fromRatios(0.338f, 0.338f, 0.662f, 0.662f))\n\n  lazy val next2Icon = List(\n    Segment().fromRatios(0.6f, 0.49f, 0.4f, 0.7f),\n    Segment().fromRatios(0.6f, 0.51f, 0.4f, 0.3f))\n\n  lazy val back2Icon = List(\n    Segment().fromRatios(0.4f, 0.51f, 0.4f, 0.3f),\n    Segment().fromRatios(0.4f, 0.49f, 0.4f, 0.7f))\n\n  val noIcon = List.empty\n\n  val iconPaint: Paint = {\n    val paint = new Paint\n    paint.setAntiAlias(true)\n    paint.setStyle(Style.STROKE)\n    paint.setStrokeWidth(defaultStroke)\n    paint.setColor(defaultColor)\n    paint\n  }\n\n  var currentTypeIcon = defaultIcon\n\n  var running = false\n\n  var currentIcon: Option[Icon] = None\n\n  var toIcon: Option[Icon] = None\n\n  var transformIcon: Option[Icon] = None\n\n  override def onBoundsChange(bounds: Rect): Unit = {\n    super.onBoundsChange(bounds)\n    size = Some(Dim(bounds.width(), bounds.height()))\n    setTypeIcon(defaultIcon)\n  }\n\n  override def draw(canvas: Canvas): Unit =\n    if (running) {\n      transformIcon.foreach(drawIcon(canvas, _))\n    } else {\n      currentIcon.foreach(drawIcon(canvas, _))\n    }\n\n  override def setColorFilter(cf: ColorFilter): Unit =\n    iconPaint.setColorFilter(cf)\n\n  override def setAlpha(alpha: Int): Unit = iconPaint.setAlpha(alpha)\n\n  override def getOpacity: Int = PixelFormat.TRANSPARENT\n\n  override def stop(): Unit = {\n    toIcon foreach setIcon\n    toIcon = None\n    running = false\n  }\n\n  override def isRunning: Boolean = running\n\n  override def start(): Unit = (toIcon, currentIcon) match {\n    case (Some(to), Some(current)) =>\n      running = true\n      moveIcon(current, to)\n    case (Some(to), None) =>\n      setIcon(to)\n      toIcon = None\n    case _ => ()\n  }\n\n  def setColor(color: Int): Unit = {\n    iconPaint.setColor(color)\n    invalidateSelf()\n  }\n\n  def setColorResource(color: Int): Unit = {\n    iconPaint.setColor(ContextCompat.getColor(context.application, color))\n    invalidateSelf()\n  }\n\n  def setStroke(stroke: Float) = {\n    iconPaint.setStrokeWidth(stroke)\n    invalidateSelf()\n  }\n\n  def setTransformIcon(icon: Icon) = {\n    transformIcon = Some(icon)\n    invalidateSelf()\n  }\n\n  def setIcon(icon: Icon) = {\n    currentIcon = Some(icon)\n    invalidateSelf()\n  }\n\n  def setToIcon(icon: Icon) = toIcon = Some(icon)\n\n  def setTypeIcon(icon: Int) = {\n    currentTypeIcon = icon\n    icon match {\n      case ADD    => setIcon(addIcon)\n      case BACK   => setIcon(backIcon)\n      case BURGER => setIcon(burgerIcon)\n      case CHECK  => setIcon(checkIcon)\n      case CLOSE  => setIcon(closeIcon)\n      case DOWN   => setIcon(downIcon)\n      case NEXT   => setIcon(nextIcon)\n      case NOICON => setIcon(noIcon)\n      case UP     => setIcon(upIcon)\n      case NEXT2  => setIcon(next2Icon)\n      case BACK2  => setIcon(back2Icon)\n    }\n  }\n\n  def setToTypeIcon(icon: Int) = {\n    currentTypeIcon = icon\n    icon match {\n      case ADD    => setToIcon(addIcon)\n      case BACK   => setToIcon(backIcon)\n      case BURGER => setToIcon(burgerIcon)\n      case CHECK  => setToIcon(checkIcon)\n      case CLOSE  => setToIcon(closeIcon)\n      case DOWN   => setToIcon(downIcon)\n      case NEXT   => setToIcon(nextIcon)\n      case NOICON => setToIcon(noIcon)\n      case UP     => setToIcon(upIcon)\n      case NEXT2  => setToIcon(next2Icon)\n      case BACK2  => setToIcon(back2Icon)\n    }\n  }\n\n  private[this] def drawIcon(canvas: Canvas, icon: Icon): Unit =\n    icon foreach (drawSegment(canvas, _))\n\n  private[this] def drawSegment(canvas: Canvas, segment: Segment): Unit = {\n    iconPaint.setAlpha((segment.alpha * 255).toInt)\n    val p1 = recalculatePointByPadding(segment.point1)\n    val p2 = recalculatePointByPadding(segment.point2)\n    canvas.drawLine(p1.x, p1.y, p2.x, p2.y, iconPaint)\n  }\n\n  private[this] def recalculatePointByPadding(pos: Point): Point =\n    size map { s =>\n      val newW = s.wight - (padding * 2)\n      val newH = s.height - (padding * 2)\n      val newX = (newW * pos.x) / s.wight\n      val newY = (newH * pos.y) / s.height\n      Point(newX + padding, newY + padding)\n    } getOrElse pos\n\n  def moveIcon(from: Icon, to: Icon) = {\n    val valueAnimator: ValueAnimator = ValueAnimator.ofInt(0, 100)\n    valueAnimator.addUpdateListener(new AnimatorUpdateListener {\n      override def onAnimationUpdate(animation: ValueAnimator): Unit = {\n        val fraction = animation.getAnimatedFraction\n\n        val fromOver = from.drop(to.length)\n        val toOver   = to.drop(from.length)\n\n        val transform = from.zip(to) map {\n          case (origin, target) => transformSegment(origin, target, fraction)\n        }\n\n        val segmentFromOver = fromOver map (_.copy(alpha = 1 - fraction))\n\n        val segmentToOver = toOver map { segment =>\n          transformSegment(\n            Segment(\n              Point(segment.point1.x + 1, segment.point1.y + 1),\n              Point(segment.point1.x, segment.point1.y)),\n            segment,\n            fraction)\n        }\n\n        val list = transform ++ segmentFromOver ++ segmentToOver\n\n        setTransformIcon(list)\n\n      }\n    })\n    valueAnimator.setInterpolator(new DecelerateInterpolator())\n    valueAnimator.addListener(new AnimatorListenerAdapter {\n      override def onAnimationEnd(animation: Animator): Unit = {\n        super.onAnimationEnd(animation)\n        stop()\n      }\n    })\n    valueAnimator.start()\n  }\n\n  def transformSegment(from: Segment, to: Segment, fraction: Float): Segment =\n    if (from.equals(to)) {\n      from\n    } else {\n      val point1 = calculatePoint(from.point1, to.point1, fraction)\n      val point2 = calculatePoint(from.point2, to.point2, fraction)\n\n      Segment(point1, point2)\n    }\n\n  def calculatePoint(from: Point, to: Point, fraction: Float): Point = {\n    val cathetiX = to.x - from.x\n    val cathetiY = to.y - from.y\n\n    val hypotenuse =\n      Math.sqrt((cathetiX * cathetiX) + (cathetiY * cathetiY)).toFloat\n    val angle = Math.atan(cathetiY / cathetiX)\n\n    val rFraction = hypotenuse * fraction\n\n    val coordX = rFraction * Math.cos(angle).toFloat\n    val coordY = rFraction * Math.sin(angle).toFloat\n\n    if (cathetiX >= 0)\n      Point(from.x + coordX, from.y + coordY)\n    else\n      Point(from.x - coordX, from.y - coordY)\n  }\n}\n\ntrait PathMorphDrawableTypes {\n  type Icon = List[Segment]\n}\n\nobject IconTypes {\n  val NOICON = 0\n  val BURGER = 1\n  val BACK   = 2\n  val CHECK  = 3\n  val ADD    = 4\n  val UP     = 5\n  val DOWN   = 6\n  val NEXT   = 7\n  val CLOSE  = 8\n  val NEXT2  = 9\n  val BACK2  = 10\n}\n\ncase class Dim(wight: Int, height: Int)\n\ncase class Point(x: Float, y: Float)\n\ncase class Segment(point1: Point = Point(0, 0), point2: Point = Point(0, 0), alpha: Float = 1) {\n\n  def fromRatios(ratioX1: Float, ratioY1: Float, ratioX2: Float, ratioY2: Float)(\n      implicit dim: Option[Dim]): Segment = {\n    val (x1: Float, y1: Float, x2: Float, y2: Float) = dim.map { value =>\n      val x1 = ratioX1 * value.wight\n      val y1 = ratioY1 * value.height\n      val x2 = ratioX2 * value.wight\n      val y2 = ratioY2 * value.height\n      (x1, y1, x2, y2)\n    }.getOrElse(0f, 0f, 0f, 0f, 0f)\n    Segment(Point(x1, y1), Point(x2, y2))\n  }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/drawables/RippleCollectionDrawable.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.drawables\n\nimport android.animation.{Animator, AnimatorListenerAdapter, ValueAnimator}\nimport android.graphics.drawable.Drawable\nimport macroid.extras.ResourcesExtras._\nimport android.graphics._\nimport android.view.animation.DecelerateInterpolator\nimport macroid.extras.SnailsUtils\nimport cards.nine.app.ui.preferences.commons.SpeedAnimations\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\n\nimport scala.concurrent.{Future, Promise}\n\nclass RippleCollectionDrawable(x: Int = 0, y: Int = 0, circleColor: Int = 0)(\n    implicit contextWrapper: ContextWrapper)\n    extends Drawable {\n\n  var percentage: Float = 0\n\n  val duration = SpeedAnimations.getDuration\n\n  lazy val circlePaint = {\n    val paint = new Paint\n    paint.setColor(circleColor)\n    paint\n  }\n\n  private[this] val animator: ValueAnimator = {\n    val valueAnimation = new ValueAnimator\n    valueAnimation.setInterpolator(new DecelerateInterpolator())\n    valueAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n      override def onAnimationUpdate(value: ValueAnimator) =\n        update(value.getAnimatedValue.asInstanceOf[Float])\n    })\n    valueAnimation\n  }\n\n  override def setColorFilter(cf: ColorFilter): Unit =\n    circlePaint.setColorFilter(cf)\n\n  override def setAlpha(alpha: Int): Unit = circlePaint.setAlpha(alpha)\n\n  override def getOpacity: Int = PixelFormat.TRANSPARENT\n\n  override def draw(canvas: Canvas): Unit = {\n    val bounds = getBounds\n    val radius =\n      SnailsUtils.calculateRadius(x, y, bounds.width(), bounds.height())\n    canvas.drawCircle(x, y, radius * percentage, circlePaint)\n  }\n\n  def start(): Ui[Future[Any]] = Ui {\n    val promise = Promise[Unit]()\n    animator.removeAllListeners()\n    animator.addListener(new AnimatorListenerAdapter() {\n      override def onAnimationEnd(animation: Animator): Unit = {\n        super.onAnimationEnd(animation)\n        promise.trySuccess(())\n      }\n    })\n    animator.setFloatValues(0f, 1f)\n    animator.setDuration(duration)\n    animator.start()\n    promise.future\n  }\n\n  private[this] def update(p: Float) = {\n    percentage = p\n    invalidateSelf()\n  }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/drawables/TopBarMomentBackgroundDrawable.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.drawables\n\nimport android.graphics._\nimport android.graphics.drawable.Drawable\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.SearchBackgroundColor\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nclass TopBarMomentBackgroundDrawable(\n    implicit theme: NineCardsTheme,\n    contextWrapper: ContextWrapper)\n    extends Drawable {\n\n  val color = theme.get(SearchBackgroundColor)\n\n  val radius = resGetDimensionPixelSize(R.dimen.radius_default)\n\n  val radiusIcon = resGetDimensionPixelSize(R.dimen.radius_icon_top_bar_moment)\n\n  val paint: Paint = {\n    val paint = new Paint(Paint.ANTI_ALIAS_FLAG)\n    paint.setColor(color)\n    paint\n  }\n\n  override def draw(canvas: Canvas): Unit = {\n    val bounds = getBounds\n    canvas.drawRoundRect(\n      new RectF(\n        bounds.left + radius,\n        bounds.top + radius,\n        bounds.right - radius,\n        bounds.bottom - radius),\n      radiusIcon,\n      radiusIcon,\n      paint)\n  }\n\n  override def setColorFilter(cf: ColorFilter): Unit = paint.setColorFilter(cf)\n\n  override def setAlpha(alpha: Int): Unit = paint.setAlpha(alpha)\n\n  override def getOpacity: Int = PixelFormat.TRANSPARENT\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/drawables/TopBarMomentEdgeBackgroundDrawable.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.drawables\n\nimport android.graphics._\nimport android.graphics.drawable.Drawable\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.SearchBackgroundColor\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nclass TopBarMomentEdgeBackgroundDrawable(\n    implicit theme: NineCardsTheme,\n    contextWrapper: ContextWrapper)\n    extends Drawable {\n\n  val color = theme.get(SearchBackgroundColor).alpha(.2f)\n\n  val radius = resGetDimensionPixelSize(R.dimen.radius_default)\n\n  val height = resGetDimensionPixelSize(R.dimen.height_icon_content_top_bar_moment) - radius\n\n  val width = resGetDimensionPixelSize(R.dimen.width_icon_content_top_bar_moment)\n\n  val size = resGetDimensionPixelSize(R.dimen.padding_large) + (width / 2) - radius\n\n  val paint: Paint = {\n    val paint = new Paint(Paint.ANTI_ALIAS_FLAG)\n    paint.setColor(color)\n    paint\n  }\n\n  override def draw(canvas: Canvas): Unit = {\n    val bounds          = getBounds\n    val verticalPadding = (bounds.height() - height) / 2\n    canvas.drawRect(\n      bounds.left,\n      verticalPadding + (radius / 2),\n      size,\n      bounds.height() - verticalPadding - (radius / 2),\n      paint)\n  }\n\n  override def setColorFilter(cf: ColorFilter): Unit = paint.setColorFilter(cf)\n\n  override def setAlpha(alpha: Int): Unit = paint.setAlpha(alpha)\n\n  override def getOpacity: Int = PixelFormat.TRANSPARENT\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/drawables/tweaks/PathMorphDrawableTweaks.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.drawables.tweaks\n\nimport android.widget.ImageView\nimport cards.nine.app.ui.components.drawables.PathMorphDrawable\nimport macroid.Tweak\n\nimport scala.util.Try\n\nobject PathMorphDrawableTweaks {\n  type W = ImageView\n\n  def pmdAnimIcon(icon: Int) = Tweak[W] { view =>\n    view.getDrawable.asInstanceOf[PathMorphDrawable].setToTypeIcon(icon)\n    view.getDrawable.asInstanceOf[PathMorphDrawable].start()\n  }\n\n  def pmdChangeIcon(icon: Int) =\n    Tweak[W](view => Try(view.getDrawable.asInstanceOf[PathMorphDrawable].setTypeIcon(icon)))\n\n  def pmdColor(color: Int) =\n    Tweak[W](view => Try(view.getDrawable.asInstanceOf[PathMorphDrawable].setColor(color)))\n\n  def pmdColorResource(color: Int) =\n    Tweak[W](view => Try(view.getDrawable.asInstanceOf[PathMorphDrawable].setColorResource(color)))\n\n  def pmdStroke(stroke: Float) =\n    Tweak[W](view => Try(view.getDrawable.asInstanceOf[PathMorphDrawable].setStroke(stroke)))\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/AnimatedWorkSpaces.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.support.v4.view.{MotionEventCompat, ViewConfigurationCompat}\nimport android.util.AttributeSet\nimport android.view.MotionEvent._\nimport android.view.ViewGroup.LayoutParams\nimport android.view.ViewGroup.LayoutParams._\nimport android.view._\nimport android.widget.FrameLayout\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.commons._\nimport cards.nine.app.ui.components.layouts.AnimatedWorkSpaces._\nimport cards.nine.app.ui.preferences.commons.WorkspaceAnimations\nimport cards.nine.commons._\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\n\nimport scala.concurrent.ExecutionContext.Implicits.global\n\nabstract class AnimatedWorkSpaces[Holder <: ViewGroup, Data](\n    context: Context,\n    attr: AttributeSet,\n    defStyleAttr: Int)\n    extends FrameLayout(context, attr, defStyleAttr)\n    with Contexts[View] { self =>\n\n  // First parameter  [Data]    : Current data of the screen\n  // Second parameter [Data]    : The data where you go\n  // Third parameter  [Boolean] : movement to left?\n  // Fourth parameter [Float]   : Fraction of the movement\n  type MovementObserver = ((Data, Data, Boolean, Float) => Unit)\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  val gestureDetector =\n    new GestureDetector(getContext, new GestureDetector.SimpleOnGestureListener() {\n      override def onLongPress(e: MotionEvent): Unit = listener.onLongClick()\n\n      override def onSingleTapConfirmed(e: MotionEvent): Boolean = {\n        listener.onClick()\n        true\n      }\n    })\n\n  val minVelocity: Int = 250\n\n  val maxRatioVelocity: Int = 3000\n\n  val maxVelocity: Int = 700\n\n  val spaceVelocity: Int = maxVelocity - minVelocity\n\n  val minimumViews = 3\n\n  val positionViewKey = \"position-view\"\n\n  var listener = new AnimatedWorkSpacesListener\n\n  var data: Seq[Data] = Seq.empty\n\n  private[this] var views: Seq[Holder] = Seq.empty\n\n  var animatedWorkspaceStatuses = AnimatedWorkSpacesStatuses()\n\n  var onPageChangedObservers: Seq[PageChangedObserver] = Seq.empty\n\n  var onMovementObservers: Seq[MovementObserver] = Seq.empty\n\n  val (touchSlop, maximumVelocity, minimumVelocity) = {\n    val configuration: ViewConfiguration = ViewConfiguration.get(getContext)\n    (ViewConfigurationCompat.getScaledPagingTouchSlop(configuration),\n     configuration.getScaledMaximumFlingVelocity,\n     configuration.getScaledMinimumFlingVelocity)\n  }\n\n  val durationAnimation = resGetInteger(android.R.integer.config_shortAnimTime)\n\n  val moveItemsAnimator = new TranslationAnimator(\n    translation = NoTranslation,\n    update = (value: Float) => {\n      animatedWorkspaceStatuses = animatedWorkspaceStatuses.copy(displacement = value)\n      applyTransforms()\n    }\n  )\n\n  val params = new LayoutParams(MATCH_PARENT, MATCH_PARENT)\n\n  var parentViewOne = slot[FrameLayout]\n\n  var parentViewTwo = slot[FrameLayout]\n\n  var parentViewThree = slot[FrameLayout]\n\n  (self <~ vgAddViews(\n    Seq(\n      (w[FrameLayout] <~\n        wire(parentViewOne) <~\n        vAddField(positionViewKey, NextView)).get,\n      (w[FrameLayout] <~\n        wire(parentViewTwo) <~\n        vAddField(positionViewKey, FrontView)).get,\n      (w[FrameLayout] <~\n        wire(parentViewThree) <~\n        vAddField(positionViewKey, PreviousView)).get),\n    params)).run\n\n  def animationPref = WorkspaceAnimations.readValue\n\n  def createEmptyView(): Holder\n\n  def createView(viewType: Int): Holder\n\n  def populateView(view: Option[Holder], data: Data, viewType: Int, position: Int): Ui[Any]\n\n  def getItemViewTypeCount: Int = 0\n\n  def getItemViewType(data: Data, position: Int): Int = 0\n\n  def getWorksSpacesCount = data.length\n\n  def getCurrentView: Option[Holder] =\n    views.lift(animatedWorkspaceStatuses.currentItem)\n\n  def getView(position: Int): Option[Holder] = views.lift(position)\n\n  def init(\n      newData: Seq[Data],\n      position: Int = 0,\n      forcePopulatePosition: Option[Int] = None): Unit = {\n\n    animatedWorkspaceStatuses = animatedWorkspaceStatuses.copy(currentItem = position)\n\n    // We creates the views for our workspace. We have a minimum of views, if our data don't have this minimum,\n    // we must create the necessary empty views\n\n    views = (newData.zipWithIndex map {\n      case (itemData, index) =>\n        val newScreen = data.lift(index).isEmpty\n        val sameData  = data.lift(index) contains itemData\n\n        lazy val newView = createView(getItemViewType(itemData, index))\n\n        val selectedView =\n          if (newScreen) newView\n          else\n            views.lift(index) match {\n              case Some(oldView) => oldView\n              case _             => newView\n            }\n\n        (sameData, forcePopulatePosition) match {\n          case (true, Some(forceIndex)) if index != forceIndex =>\n          case _ =>\n            populateView(Some(selectedView), itemData, getItemViewType(itemData, index), index).run\n        }\n        selectedView\n    }) ++ (newData.length until minimumViews map (_ => createEmptyView()))\n\n    data = newData\n\n    ((parentViewOne <~ vgRemoveAllViews) ~\n      (parentViewTwo <~ vgRemoveAllViews) ~\n      (parentViewThree <~ vgRemoveAllViews) ~\n      reset()).run\n\n  }\n\n  def clean(): Unit = {\n    data = Seq.empty\n    views = Seq.empty\n    ((parentViewOne <~ vgRemoveAllViews) ~\n      (parentViewTwo <~ vgRemoveAllViews) ~\n      (parentViewThree <~ vgRemoveAllViews)).run\n  }\n\n  def goToItem(): Int =\n    (animatedWorkspaceStatuses.displacement, animatedWorkspaceStatuses.currentItem) match {\n      case (disp, item) if disp < 0 && item >= data.size - 1 => 0\n      case (disp, item) if disp < 0                          => item + 1\n      case (_, item) if item <= 0                            => data.length - 1\n      case (_, item)                                         => item - 1\n    }\n\n  def notifyPageChangedObservers() =\n    onPageChangedObservers foreach (observer => observer(goToItem()))\n\n  def addPageChangedObservers(f: PageChangedObserver) =\n    onPageChangedObservers = onPageChangedObservers :+ f\n\n  def notifyMovementObservers(percent: Float) =\n    for {\n      from <- data.lift(currentPage())\n      to   <- data.lift(goToItem())\n    } yield {\n      onMovementObservers foreach (observer =>\n                                     observer(\n                                       from,\n                                       to,\n                                       animatedWorkspaceStatuses.isFromLeft,\n                                       percent))\n    }\n\n  def addMovementObservers(f: MovementObserver) =\n    onMovementObservers = onMovementObservers :+ f\n\n  protected def getSizeWidget = getWidth\n\n  def isPosition(position: Int): Boolean =\n    animatedWorkspaceStatuses.currentItem == position\n\n  def currentPage(): Int = animatedWorkspaceStatuses.currentItem\n\n  def isFirst: Boolean = isPosition(0)\n\n  def isLast: Boolean = isPosition(data.length - 1)\n\n  def canGoToPrevious = !isFirst\n\n  def canGoToNext = !isLast\n\n  private[this] def snap(velocity: Float): Ui[Any] = {\n    moveItemsAnimator.cancel()\n    val destiny = (velocity, animatedWorkspaceStatuses.displacement) match {\n      case (v, d) if v > 0 && d > 0  => getSizeWidget\n      case (v, d) if v <= 0 && d < 0 => -getSizeWidget\n      case _                         => 0\n    }\n    animateViews(destiny, calculateDurationByVelocity(velocity, durationAnimation))\n  }\n  protected def calculateDurationByVelocity(velocity: Float, defaultVelocity: Int): Int = {\n    velocity match {\n      case 0 => defaultVelocity\n      case _ =>\n        (spaceVelocity - ((math.min(math.abs(velocity), maxRatioVelocity) * spaceVelocity) / maxRatioVelocity) + minVelocity).toInt\n    }\n  }\n\n  private[this] def snapDestination(): Ui[Any] = {\n    val destiny = animatedWorkspaceStatuses.displacement match {\n      case d if d > getSizeWidget * .6f  => getSizeWidget\n      case d if d < -getSizeWidget * .6f => -getSizeWidget\n      case _                             => 0\n    }\n    animateViews(destiny, durationAnimation)\n  }\n\n  private[this] def performScroll(delta: Float): Ui[Any] = {\n    moveItemsAnimator.cancel()\n    animatedWorkspaceStatuses = animatedWorkspaceStatuses.updateDisplacement(getSizeWidget, delta)\n    applyTransforms()\n  }\n\n  def selectPosition(position: Int): Unit = {\n    animatedWorkspaceStatuses = animatedWorkspaceStatuses.copy(currentItem = position)\n    (reset() ~ reset()).run // TODO Change that\n  }\n\n  def applyTransforms(): Ui[Any] =\n    transformOutPanelDefault() ~ transformInPanelDefault()\n\n  private[this] def transformOutPanelDefault(): Ui[Any] =\n    getFrontView <~ vTranslationX(animatedWorkspaceStatuses.displacement)\n\n  private[this] def transformInPanelDefault(): Ui[Any] = {\n    val percent  = animatedWorkspaceStatuses.percent(getSizeWidget)\n    val fromLeft = animatedWorkspaceStatuses.isFromLeft\n    val view     = if (fromLeft) getPreviousView else getNextView\n    notifyMovementObservers(percent)\n\n    val translate = {\n      val start = if (fromLeft) -getSizeWidget else getSizeWidget\n      start - (start * percent)\n    }\n\n    view <~ vTranslationX(translate)\n  }\n\n  private[this] def animateViews(dest: Int, duration: Int): Ui[Any] = {\n    animatedWorkspaceStatuses = animatedWorkspaceStatuses.copy(swap = dest != 0)\n    if (animatedWorkspaceStatuses.swap) notifyPageChangedObservers()\n    (self <~ vInvalidate) ~\n      (getFrontView <~~ moveItemsAnimator\n        .move(animatedWorkspaceStatuses.displacement, dest, duration, attachTarget = true)) ~~\n      resetAnimationEnd\n  }\n\n  private[this] def swapViews(): Ui[Any] = {\n    animatedWorkspaceStatuses = animatedWorkspaceStatuses.copy(currentItem = goToItem())\n    (this <~ (if (animatedWorkspaceStatuses.isFromLeft)\n                reloadPreviousPositionView\n              else reloadNextPositionView)) ~\n      (if (animatedWorkspaceStatuses.isFromLeft)\n         recreate(PreviousView) ~ resetView(NextView)\n       else\n         recreate(NextView) ~ resetView(PreviousView)) ~\n      Ui {\n        animatedWorkspaceStatuses = animatedWorkspaceStatuses\n          .copy(displacement = 0, enabled = data.nonEmpty && data.length > 1)\n      }\n  }\n\n  private[this] def resetAnimationEnd(): Ui[Any] =\n    (if (animatedWorkspaceStatuses.swap) swapViews() else Ui.nop) ~ layerHardware(true)\n\n  def reset(): Ui[Any] = {\n    animatedWorkspaceStatuses =\n      animatedWorkspaceStatuses.copy(displacement = 0, enabled = data.nonEmpty && data.length > 1)\n    moveItemsAnimator.cancel()\n    recreate(FrontView) ~\n      recreate(PreviousView) ~\n      recreate(NextView)\n  }\n\n  def resetItem(position: Int): Ui[Any] =\n    getPositionView(position) match {\n      case Some(positionView) =>\n        val itemData = data(position)\n        populateView(\n          Option(views(position)),\n          itemData,\n          getItemViewType(itemData, position),\n          position) ~\n          recreate(positionView, resetPosition = false)\n      case _ => Ui.nop\n    }\n\n  private[this] def recreate(positionView: PositionView, resetPosition: Boolean = true): Ui[Any] = {\n    val currentItem = animatedWorkspaceStatuses.currentItem\n\n    val position = positionView match {\n      case PreviousView =>\n        if (currentItem - 1 < 0) views.length - 1 else currentItem - 1\n      case NextView =>\n        if (currentItem + 1 > views.length - 1) 0 else currentItem + 1\n      case FrontView => currentItem\n    }\n\n    val view = getView(positionView)\n\n    (view <~\n      vgRemoveAllViews <~\n      vgAddView(views(position), params)) ~\n      (if (resetPosition) resetView(positionView) else Ui.nop)\n  }\n\n  def resetView(positionView: PositionView) = {\n    val view = getView(positionView)\n    val displacement = positionView match {\n      case PreviousView => -getSizeWidget\n      case NextView     => getSizeWidget\n      case FrontView    => 0\n    }\n    view <~ vTranslationX(displacement) <~ vScaleX(1) <~ vScaleY(1) <~ vAlpha(1)\n  }\n\n  override def onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int): Unit = {\n    super.onSizeChanged(w, h, oldw, oldh)\n    animatedWorkspaceStatuses = animatedWorkspaceStatuses.copy(dimen = Dimen(w, h))\n  }\n\n  override def onInterceptTouchEvent(event: MotionEvent): Boolean = {\n    super.onInterceptTouchEvent(event)\n    val (action, x, y) = updateTouch(event)\n    (action, animatedWorkspaceStatuses.touchState) match {\n      case (ACTION_MOVE, Scrolling) =>\n        requestDisallowInterceptTouchEvent(true)\n        true\n      case (ACTION_MOVE, _) =>\n        setStateIfNeeded(x, y)\n        !animatedWorkspaceStatuses.enabled || animatedWorkspaceStatuses.isScrolling\n      case (ACTION_DOWN, _) =>\n        animatedWorkspaceStatuses =\n          animatedWorkspaceStatuses.copy(lastMotionX = x, lastMotionY = y)\n        animatedWorkspaceStatuses.isScrolling\n      case (ACTION_CANCEL | ACTION_UP, _) =>\n        computeFling()\n        animatedWorkspaceStatuses = animatedWorkspaceStatuses.copy(touchState = Stopped)\n        !animatedWorkspaceStatuses.enabled || animatedWorkspaceStatuses.isScrolling\n      case _ =>\n        !animatedWorkspaceStatuses.enabled || animatedWorkspaceStatuses.isScrolling\n    }\n  }\n\n  override def onTouchEvent(event: MotionEvent): Boolean = {\n    super.onTouchEvent(event)\n    val (action, x, y) = updateTouch(event)\n    gestureDetector.onTouchEvent(event)\n    (action, animatedWorkspaceStatuses.touchState) match {\n      case (ACTION_MOVE, Scrolling) =>\n        requestDisallowInterceptTouchEvent(true)\n        val deltaX = animatedWorkspaceStatuses.deltaX(x)\n        animatedWorkspaceStatuses =\n          animatedWorkspaceStatuses.copy(lastMotionX = x, lastMotionY = y)\n        if (overScroll(deltaX)) {\n          notifyMovementObservers(0f)\n          resetView(FrontView).run\n        } else {\n          performScroll(deltaX).run\n        }\n      case (ACTION_MOVE, Stopped) => setStateIfNeeded(x, y)\n      case (ACTION_DOWN, _) =>\n        animatedWorkspaceStatuses =\n          animatedWorkspaceStatuses.copy(lastMotionX = x, lastMotionY = y)\n      case (ACTION_CANCEL | ACTION_UP, _) =>\n        computeFling()\n        animatedWorkspaceStatuses = animatedWorkspaceStatuses.copy(touchState = Stopped)\n      case _ =>\n    }\n    true\n  }\n\n  protected def updateTouch(event: MotionEvent) = {\n    if (animatedWorkspaceStatuses.velocityTracker.isEmpty)\n      animatedWorkspaceStatuses =\n        animatedWorkspaceStatuses.copy(velocityTracker = Some(VelocityTracker.obtain()))\n    animatedWorkspaceStatuses.velocityTracker foreach (_.addMovement(event))\n    val action = MotionEventCompat.getActionMasked(event)\n    val x      = MotionEventCompat.getX(event, 0)\n    val y      = MotionEventCompat.getY(event, 0)\n    (action, x, y)\n  }\n\n  private[this] def overScroll(deltaX: Float): Boolean =\n    (animatedWorkspaceStatuses.displacement, deltaX) match {\n      case (x, dx) if isFirst && dx < 0 && x - dx >= 0 => true\n      case (x, dx) if isLast && dx > 0 && x - dx <= 0  => true\n      case _                                           => false\n    }\n\n  def setStateIfNeeded(x: Float, y: Float) = {\n    if (animatedWorkspaceStatuses.enabled) {\n      val xDiff = math.abs(x - animatedWorkspaceStatuses.lastMotionX)\n      val yDiff = math.abs(y - animatedWorkspaceStatuses.lastMotionY)\n\n      val xMoved = xDiff > touchSlop\n      val yMoved = yDiff > touchSlop\n\n      if (xMoved || yMoved) {\n        val penultimate = data.length - 2\n        val isScrolling = (xDiff > yDiff, moveItemsAnimator.isRunning) match {\n          case (true, true) if x - animatedWorkspaceStatuses.lastMotionX > 0 && isPosition(1) =>\n            false\n          case (true, true)\n              if x - animatedWorkspaceStatuses.lastMotionX < 0 && isPosition(penultimate) =>\n            false\n          case (false, true) if y - animatedWorkspaceStatuses.lastMotionY > 0 && isPosition(1) =>\n            false\n          case (false, true)\n              if y - animatedWorkspaceStatuses.lastMotionY < 0 && isPosition(penultimate) =>\n            false\n          case (true, _) if x - animatedWorkspaceStatuses.lastMotionX > 0 && !isFirst =>\n            true\n          case (true, _) if x - animatedWorkspaceStatuses.lastMotionX < 0 && !isLast =>\n            true\n          case (false, _) if y - animatedWorkspaceStatuses.lastMotionY > 0 && !isFirst =>\n            true\n          case (false, _) if y - animatedWorkspaceStatuses.lastMotionY < 0 && !isLast =>\n            true\n          case _ => false\n        }\n        if (isScrolling) {\n          animatedWorkspaceStatuses = animatedWorkspaceStatuses.copy(touchState = Scrolling)\n          layerHardware(true).run\n        }\n        animatedWorkspaceStatuses =\n          animatedWorkspaceStatuses.copy(lastMotionX = x, lastMotionY = y)\n      }\n    }\n  }\n\n  private[this] def layerHardware(activate: Boolean) =\n    (getFrontView <~ vLayerHardware(activate = activate)) ~\n      (getNextView <~ vLayerHardware(activate = activate)) ~\n      (getPreviousView <~ vLayerHardware(activate = activate))\n\n  private[this] def computeFling() =\n    animatedWorkspaceStatuses.velocityTracker foreach { tracker =>\n      tracker.computeCurrentVelocity(1000, maximumVelocity)\n      val velocity = tracker.getXVelocity\n      ((animatedWorkspaceStatuses.isScrolling, math.abs(velocity) > minimumVelocity) match {\n        case (true, true) => snap(velocity)\n        case _            => snapDestination()\n      }).run\n      tracker.recycle()\n      animatedWorkspaceStatuses = animatedWorkspaceStatuses.copy(velocityTracker = None)\n    }\n\n  private[this] def reloadPreviousPositionView = Transformer {\n    case fl: FrameLayout if fl.getField[PositionView](positionViewKey).contains(PreviousView) =>\n      fl <~ vAddField(positionViewKey, FrontView)\n    case fl: FrameLayout if fl.getField[PositionView](positionViewKey).contains(NextView) =>\n      fl <~ vAddField(positionViewKey, PreviousView)\n    case fl: FrameLayout if fl.getField[PositionView](positionViewKey).contains(FrontView) =>\n      fl <~ vAddField(positionViewKey, NextView)\n  }\n\n  private[this] def reloadNextPositionView = Transformer {\n    case fl: FrameLayout if fl.getField[PositionView](positionViewKey).contains(PreviousView) =>\n      fl <~ vAddField(positionViewKey, NextView)\n    case fl: FrameLayout if fl.getField[PositionView](positionViewKey).contains(NextView) =>\n      fl <~ vAddField(positionViewKey, FrontView)\n    case fl: FrameLayout if fl.getField[PositionView](positionViewKey).contains(FrontView) =>\n      fl <~ vAddField(positionViewKey, PreviousView)\n  }\n\n  protected def getPreviousView: Option[FrameLayout] = getView(PreviousView)\n\n  protected def getNextView: Option[FrameLayout] = getView(NextView)\n\n  protected def getFrontView: Option[FrameLayout] = getView(FrontView)\n\n  private[this] def getView(positionView: PositionView): Option[FrameLayout] = {\n    (parentViewThree flatMap (_.getField[PositionView](positionViewKey)),\n     parentViewOne flatMap (_.getField[PositionView](positionViewKey)),\n     parentViewTwo flatMap (_.getField[PositionView](positionViewKey))) match {\n      case (Some(`positionView`), _, _) => parentViewThree\n      case (_, Some(`positionView`), _) => parentViewOne\n      case (_, _, Some(`positionView`)) => parentViewTwo\n      case _                            => None\n    }\n  }\n\n  private[this] def getPositionView(position: Int): Option[PositionView] = {\n    val currentItem = currentPage()\n    if (currentItem == position) {\n      Option(FrontView)\n    } else if (currentItem - 1 == position) {\n      Option(PreviousView)\n    } else if (currentItem + 1 == position) {\n      Option(NextView)\n    } else {\n      None\n    }\n  }\n\n}\n\ncase class AnimatedWorkSpacesStatuses(\n    dimen: Dimen = Dimen(),\n    touchState: ViewState = Stopped,\n    enabled: Boolean = false,\n    velocityTracker: Option[VelocityTracker] = None,\n    lastMotionX: Float = 0,\n    lastMotionY: Float = 0,\n    swap: Boolean = false,\n    displacement: Float = 0,\n    currentItem: Int = 0) {\n\n  def deltaX(x: Float): Float = lastMotionX - x\n\n  def deltaY(y: Float): Float = lastMotionY - y\n\n  def isStopped = touchState == Stopped\n\n  def isScrolling = touchState == Scrolling\n\n  def updateDisplacement(size: Int, delta: Float): AnimatedWorkSpacesStatuses =\n    copy(displacement = math.max(-size, Math.min(size, displacement - delta)))\n\n  def percent(size: Int): Float = math.abs(displacement) / size\n\n  def totalXPercent(size: Int, numberOfScreens: Int): Float = {\n    val stepX = math.abs(displacement) / size\n    val curX  = currentItem + (if (isFromLeft) -stepX else stepX)\n    curX / numberOfScreens\n  }\n\n  def isFromLeft = displacement > 0\n\n}\n\nobject AnimatedWorkSpaces {\n  // First parameter [Int]: Position of the screen\n  type PageChangedObserver = (Int => Unit)\n}\n\ncase class AnimatedWorkSpacesListener(\n    onClick: () => Unit = () => (),\n    onLongClick: () => Unit = () => ())\n\ncase class Dimen(width: Int = 0, height: Int = 0)\n\nsealed trait PositionView\n\ncase object PreviousView extends PositionView\n\ncase object NextView extends PositionView\n\ncase object FrontView extends PositionView\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/AppsMomentLayout.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.LinearLayout\nimport cards.nine.app.ui.commons.ops.CollectionOps._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.components.layouts.tweaks.WorkSpaceButtonTweaks._\nimport cards.nine.app.ui.components.models.LauncherMoment\nimport cards.nine.app.ui.launcher.LauncherActivity\nimport cards.nine.app.ui.launcher.jobs.NavigationJobs\nimport cards.nine.commons.javaNull\nimport cards.nine.models.types.NineCardsMoment\nimport cards.nine.models.types.theme.DrawerBackgroundColor\nimport cards.nine.models.{Card, Collection, NineCardsTheme}\nimport cards.nine.process.intents.LauncherExecutorProcessPermissionException\nimport macroid.extras.DeviceVersion.Lollipop\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nclass AppsMomentLayout(context: Context, attrs: AttributeSet, defStyle: Int)\n    extends LinearLayout(context, attrs, defStyle)\n    with Contexts[View]\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attrs: AttributeSet) = this(context, attrs, 0)\n\n  // TODO First implementation in order to remove LauncherPresenter\n  val navigationJobs: NavigationJobs = context match {\n    case activity: LauncherActivity => activity.navigationJobs\n    case _                          => throw new RuntimeException(\"NavigationJobs not found\")\n  }\n\n  LayoutInflater.from(context).inflate(R.layout.apps_moment_layout, this)\n\n  lazy val iconContent = findView(TR.moment_bar_icon_content)\n\n  lazy val icon = findView(TR.moment_bar_icon)\n\n  lazy val appsContent = findView(TR.moment_bar_apps)\n\n  (Lollipop.ifSupportedThen(iconContent <~ vElevation(\n    resGetDimensionPixelSize(R.dimen.elevation_default))) getOrElse Ui.nop).run\n\n  def populate(moment: LauncherMoment)(\n      implicit context: ActivityContextWrapper,\n      theme: NineCardsTheme): Ui[Any] = moment.collection match {\n    case Some(collection: Collection) =>\n      val resIcon = collection.getIconDetail\n      val color   = theme.getIndexColor(collection.themedColorIndex)\n      (this <~\n        vBackgroundColor(theme.get(DrawerBackgroundColor))) ~\n        (iconContent <~\n          vBackgroundColor(color) <~\n          On.click {\n            Ui(navigationJobs.navigationUiActions.goToMomentWorkspace().resolveAsync())\n          }) ~\n        (icon <~\n          ivSrc(resIcon)) ~\n        (appsContent <~\n          vgRemoveAllViews <~\n          vgAddViews(collection.cards map (createIconCard(_, moment.momentType))))\n    case _ =>\n      val blank: Drawable = javaNull\n      (this <~ vBlankBackground) ~\n        (iconContent <~ vBlankBackground) ~\n        (appsContent <~ vgRemoveAllViews) ~\n        (icon <~ ivSrc(blank))\n  }\n\n  def setPaddingTopAndBottom(paddingTop: Int, paddingBottom: Int) =\n    (iconContent <~ vPadding(paddingTop = paddingTop)) ~\n      (appsContent <~ vPadding(paddingBottom = paddingBottom))\n\n  private[this] def createIconCard(card: Card, moment: Option[NineCardsMoment])(\n      implicit contextWrapper: ActivityContextWrapper,\n      theme: NineCardsTheme): WorkSpaceButton =\n    (w[WorkSpaceButton] <~\n      vMatchWidth <~\n      wbInit(WorkSpaceAppMomentButton) <~\n      wbPopulateCard(card) <~\n      On.click {\n        Ui(navigationJobs.openMomentIntent(card, moment).resolveAsyncServiceOr[Throwable] {\n          case e: LauncherExecutorProcessPermissionException =>\n            navigationJobs.openMomentIntentException(card.intent.extractPhone())\n          case _ => navigationJobs.navigationUiActions.showContactUsError()\n        })\n      }).get\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/CollectionActionsPanelLayout.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.DragEvent._\nimport android.view.{LayoutInflater, View}\nimport android.widget.LinearLayout\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.widgets.TintableButton\nimport cards.nine.app.ui.components.widgets.tweaks.TintableButtonTweaks._\nimport cards.nine.app.ui.launcher.LauncherActivity\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.app.ui.launcher.jobs.{DragJobs, NavigationJobs, WidgetsJobs}\nimport cards.nine.commons.javaNull\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.PrimaryColor\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid._\nimport macroid.extras.ViewTweaks._\nimport macroid.extras.TextViewTweaks._\n\nclass CollectionActionsPanelLayout(context: Context, attrs: AttributeSet, defStyle: Int)\n    extends LinearLayout(context, attrs, defStyle)\n    with Contexts[View]\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attrs: AttributeSet) = this(context, attrs, 0)\n\n  val dragJobs: DragJobs = context match {\n    case activity: LauncherActivity => activity.dragJobs\n    case _                          => throw new RuntimeException(\"DragJobs not found\")\n  }\n\n  val navigationJobs: NavigationJobs = context match {\n    case activity: LauncherActivity => activity.navigationJobs\n    case _                          => throw new RuntimeException(\"NavigationJobs not found\")\n  }\n\n  val widgetJobs: WidgetsJobs = context match {\n    case activity: LauncherActivity => activity.widgetJobs\n    case _                          => throw new RuntimeException(\"WidgetsJobs not found\")\n  }\n\n  val unselectedPosition = -1\n\n  val selectedScale = 1.1f\n\n  val defaultScale = 1f\n\n  LayoutInflater.from(context).inflate(R.layout.collections_actions_view_panel, this)\n\n  var actions: Seq[CollectionActionItem] = Seq.empty\n\n  var draggingTo: Option[Int] = None\n\n  lazy val leftContentView = findView(TR.launcher_collections_content_1)\n\n  lazy val rightContentView = findView(TR.launcher_collections_content_2)\n\n  lazy val leftActionView = findView(TR.launcher_collections_action_1)\n\n  lazy val rightActionView = findView(TR.launcher_collections_action_2)\n\n  def load(actions: Seq[CollectionActionItem])(implicit theme: NineCardsTheme): Ui[Any] = {\n\n    def populate(action: CollectionActionItem, position: Int): Tweak[TintableButton] =\n      tvText(action.name) +\n        tvCompoundDrawablesWithIntrinsicBoundsResources(left = action.resource) +\n        vSetPosition(position) +\n        tbPressedColor(theme.get(PrimaryColor)) +\n        tbResetColor\n\n    def contentByIndex(index: Int) = index match {\n      case 0 => Option(leftContentView)\n      case 1 => Option(rightContentView)\n      case _ => None\n    }\n\n    def buttonByIndex(index: Int) = index match {\n      case 0 => Option(leftActionView)\n      case 1 => Option(rightActionView)\n      case _ => None\n    }\n\n    this.actions = actions\n\n    if (actions.length == 1) {\n      (actions.headOption match {\n        case Some(action) => leftActionView <~ populate(action, 0)\n        case _            => Ui.nop\n      }) ~ (rightContentView <~ vGone)\n    } else {\n      Ui.sequence(actions.zipWithIndex map {\n        case (action, index) =>\n          (contentByIndex(index) <~ vVisible) ~ (buttonByIndex(index) <~ populate(action, index))\n      }: _*)\n    }\n  }\n\n  def dragController(action: Int, x: Float, y: Float)(implicit theme: NineCardsTheme): Unit = {\n\n    def performAction(action: CollectionActionItem) =\n      (action.collectionActionType, statuses.collectionReorderMode) match {\n        case (CollectionActionAppInfo, _) =>\n          dragJobs\n            .settingsInAddItem()\n            .resolveAsyncServiceOr(_ => dragJobs.dragUiActions.endAddItem())\n        case (CollectionActionUninstall, _) =>\n          dragJobs\n            .uninstallInAddItem()\n            .resolveAsyncServiceOr(_ => dragJobs.dragUiActions.endAddItem())\n        case (CollectionActionRemove, _) =>\n          dragJobs.removeCollectionInReorderMode().resolveAsync()\n        case (CollectionActionEdit, Some(collection)) =>\n          navigationJobs.launchCreateOrCollection(Option(collection.id)).resolveAsync()\n        case (CollectionActionRemoveDockApp, _) =>\n          (for {\n            _ <- dragJobs.dockAppsUiActions.reset()\n            _ <- dragJobs.dragUiActions.endAddItem()\n          } yield ()).resolveAsync()\n        case (WidgetActionRemove, _) =>\n          widgetJobs.showDialogForDeletingWidget(statuses.idWidget).resolveAsync()\n        case _ => dragJobs.dragUiActions.endAddItem().resolveAsync()\n      }\n\n    action match {\n      case ACTION_DRAG_LOCATION =>\n        val newPosition = Some(calculatePosition(x))\n        if (newPosition != draggingTo) {\n          draggingTo = newPosition\n          (this <~ (draggingTo map select getOrElse select(unselectedPosition))).run\n        }\n      case ACTION_DROP =>\n        if (actions.length == 1) {\n          actions.headOption foreach performAction\n        } else {\n          draggingTo flatMap actions.lift match {\n            case Some(action: CollectionActionItem) => performAction(action)\n            case _                                  =>\n          }\n        }\n        draggingTo = None\n        (this <~ select(unselectedPosition)).run\n      case ACTION_DRAG_EXITED =>\n        draggingTo = None\n        (this <~ select(unselectedPosition)).run\n      case ACTION_DRAG_ENDED =>\n        draggingTo = None\n        (this <~ select(unselectedPosition)).run\n      case _ =>\n    }\n  }\n\n  private[this] def calculatePosition(x: Float): Int =\n    x.toInt / (getWidth / actions.length)\n\n  private[this] def select(position: Int) = Transformer {\n    case view: TintableButton if view.getPosition.contains(position) =>\n      Ui(view.setPressedColor())\n    case view: TintableButton => Ui(view.setDefaultColor())\n  }\n\n}\n\ncase class CollectionActionItem(\n    name: String,\n    resource: Int,\n    collectionActionType: CollectionActionType)\n\nsealed trait CollectionActionType\n\ncase object CollectionActionAppInfo extends CollectionActionType\n\ncase object CollectionActionUninstall extends CollectionActionType\n\ncase object CollectionActionRemove extends CollectionActionType\n\ncase object CollectionActionRemoveDockApp extends CollectionActionType\n\ncase object CollectionActionEdit extends CollectionActionType\n\ncase object WidgetActionRemove extends CollectionActionType\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/DialogToolbar.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.FrameLayout\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.commons._\nimport cards.nine.models.types.{DialogToolbarSearch, DialogToolbarTitle, DialogToolbarType}\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid._\nimport macroid.extras.EditTextTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ToolbarTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\n\nclass DialogToolbar(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends FrameLayout(context, attr, defStyleAttr)\n    with TypedFindView\n    with Contexts[View] { self =>\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  LayoutInflater.from(context).inflate(R.layout.toolbar_dialog, this)\n\n  lazy val toolbar = findView(TR.actions_toolbar_widget)\n\n  lazy val title = findView(TR.actions_toolbar_title)\n\n  lazy val search = findView(TR.actions_toolbar_search)\n\n  lazy val extendedContent = findView(TR.actions_toolbar_extended_content)\n\n  val closeDrawable = PathMorphDrawable(\n    defaultIcon = IconTypes.CLOSE,\n    defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_default),\n    padding = resGetDimensionPixelSize(R.dimen.padding_icon_home_indicator))\n\n  def init(color: Int, dialogToolbarType: DialogToolbarType = DialogToolbarTitle)(\n      implicit contextWrapper: ContextWrapper) = {\n    (dialogToolbarType match {\n      case DialogToolbarTitle => (title <~ vVisible) ~ (search <~ vGone)\n      case DialogToolbarSearch =>\n        (search <~ vVisible <~ vClearFocus) ~ (title <~ vGone)\n    }) ~\n      (toolbar <~\n        tbNavigationIcon(closeDrawable)) ~\n      (this <~\n        vBackgroundColor(color))\n  }\n\n  def changeToolbarHeight(height: Int): Ui[Any] =\n    toolbar <~ tbChangeHeightLayout(height)\n\n  def addExtendedView(view: View): Ui[Any] = extendedContent <~ vgAddView(view)\n\n  def changeIcon(icon: Int): Ui[Any] =\n    Ui {\n      closeDrawable.setToTypeIcon(icon)\n      closeDrawable.start()\n    }\n\n  def changeText(res: Int): Ui[Any] = title <~ tvText(res)\n\n  def changeText(text: String): Ui[Any] = title <~ tvText(text)\n\n  def changeSearchText(res: Int): Ui[Any] = search <~ tvText(res)\n\n  def changeSearchText(text: String = \"\"): Ui[Any] = search <~ tvText(text)\n\n  def onSearchTextChangedListener(onChanged: (String, Int, Int, Int) => Unit): Ui[Any] =\n    search <~ etAddTextChangedListener(onChanged)\n\n  def clickActionSearch(performSearch: (String) => Unit) =\n    search <~ etClickActionSearch(performSearch)\n\n  def hideKeyboardSearchText(): Ui[Any] = search <~ etHideKeyboard\n\n  def navigationClickListener(click: (View) => Ui[Any]): Ui[Any] =\n    toolbar <~ tbNavigationOnClickListener(click)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/DockAppsPanelLayout.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.graphics.Color\nimport android.util.AttributeSet\nimport android.view.DragEvent._\nimport android.view.{LayoutInflater, View}\nimport android.widget.LinearLayout\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.app.ui.components.widgets.TintableImageView\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.app.ui.launcher.LauncherActivity\nimport cards.nine.app.ui.launcher.jobs.{DragJobs, NavigationJobs}\nimport cards.nine.app.ui.launcher.types.{AddItemToCollection, AppDrawerIconShadowBuilder}\nimport cards.nine.commons._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.DockAppData\nimport cards.nine.models.types.theme.DockPressedColor\nimport cards.nine.models.types.{AppDockType, ContactDockType}\nimport cards.nine.process.intents.LauncherExecutorProcessPermissionException\nimport cards.nine.models.NineCardsTheme\nimport cats.implicits._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nclass DockAppsPanelLayout(context: Context, attrs: AttributeSet, defStyle: Int)\n    extends LinearLayout(context, attrs, defStyle)\n    with Contexts[View]\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attrs: AttributeSet) = this(context, attrs, 0)\n\n  val dragJobs: DragJobs = context match {\n    case activity: LauncherActivity => activity.dragJobs\n    case _                          => throw new RuntimeException(\"DragJobs not found\")\n  }\n\n  val navigationJobs: NavigationJobs = context match {\n    case activity: LauncherActivity => activity.navigationJobs\n    case _                          => throw new RuntimeException(\"NavigationJobs not found\")\n  }\n\n  case class State(\n      dockApps: Seq[DockAppData] = Seq.empty,\n      draggingFrom: Option[Int] = None,\n      draggingTo: Option[Int] = None) {\n\n    def getDockApp(position: Int): Option[DockAppData] =\n      dockApps.find(_.position == position)\n\n    def startDrag(position: Int): State =\n      copy(dockApps = dockApps filterNot (_.position == position), draggingFrom = Option(position))\n\n    def reload(dockApp: DockAppData): State =\n      copy(dockApps = (state.dockApps filterNot (_.position == dockApp.position)) :+ dockApp)\n\n    def reset(): State = copy(draggingTo = None, draggingFrom = None)\n\n  }\n\n  var state = State()\n\n  val unselectedPosition = -1\n\n  val selectedScale = 1.1f\n\n  val defaultScale = 1f\n\n  val selectedAlpha = .4f\n\n  val defaultAlpha = 1f\n\n  val numberOfItems = 5\n\n  val appDrawerPosition = 2\n\n  lazy val noFoundAppDrawable = PathMorphDrawable(\n    defaultIcon = IconTypes.ADD,\n    defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_default),\n    defaultColor = Color.WHITE.alpha(selectedAlpha),\n    padding = resGetDimensionPixelSize(R.dimen.padding_icon_home_indicator))\n\n  LayoutInflater.from(context).inflate(R.layout.app_drawer_panel, this)\n\n  def init(\n      apps: Seq[DockAppData])(implicit theme: NineCardsTheme, uiContext: UiContext[_]): Ui[Any] = {\n\n    def dockAppStyle(position: Int): Tweak[TintableImageView] =\n      FuncOn.longClick { view: View =>\n        state.dockApps find (_.position == position) match {\n          case Some(dockApp: DockAppData) =>\n            dragJobs\n              .startAddItemToCollection(dockApp)\n              .resolveAsync(\n                onResult = (_) => {\n                  val tintableImageView = view.asInstanceOf[TintableImageView]\n                  state = state.startDrag(position)\n                  (tintableImageView <~\n                    vStartDrag(AddItemToCollection, new AppDrawerIconShadowBuilder(view)) <~\n                    populate(state.getDockApp(position))).run\n                }\n              )\n            Ui(true)\n          case _ => Ui(true)\n        }\n      } + vSetPosition(position) + populate(state.getDockApp(position))\n\n    state = state.copy(dockApps = apps)\n    (findView(TR.launcher_page_1) <~ dockAppStyle(0)) ~\n      (findView(TR.launcher_page_2) <~ dockAppStyle(1)) ~\n      (findView(TR.launcher_page_3) <~ dockAppStyle(2)) ~\n      (findView(TR.launcher_page_4) <~ dockAppStyle(3))\n  }\n\n  def reload(\n      dockApp: DockAppData)(implicit theme: NineCardsTheme, uiContext: UiContext[_]): Ui[Any] = {\n    state = state.reload(dockApp)\n    this <~ updatePosition(dockApp.position)\n  }\n\n  def reset(): Ui[Any] = Ui(state = state.reset())\n\n  def dragAddItemController(action: Int, x: Float, y: Float): Unit =\n    action match {\n      case ACTION_DRAG_LOCATION =>\n        val newPosition = calculatePosition(x)\n        if (newPosition != state.draggingTo) {\n          state = state.copy(draggingTo = newPosition)\n          (this <~ (state.draggingTo map select getOrElse select(unselectedPosition))).run\n        }\n      case ACTION_DROP =>\n        (state.draggingFrom, state.draggingTo) match {\n          case (Some(from), Some(to)) =>\n            (for {\n              _ <- dragJobs.changePositionDockApp(to, from)\n              _ <- dragJobs.endAddItemToDockApp(to)\n            } yield ()).resolveAsyncServiceOr(\n              _ =>\n                dragJobs.dragUiActions.endAddItem() *> dragJobs.navigationUiActions\n                  .showContactUsError())\n          case (None, Some(to)) =>\n            dragJobs\n              .endAddItemToDockApp(to)\n              .resolveAsyncServiceOr(\n                _ =>\n                  dragJobs.dragUiActions.endAddItem() *> dragJobs.navigationUiActions\n                    .showContactUsError())\n          case _ => dragJobs.endAddItem().resolveAsync()\n        }\n        state = state.reset()\n        (this <~ select(unselectedPosition)).run\n      case ACTION_DRAG_EXITED =>\n        state = state.copy(draggingTo = None)\n        (this <~ select(unselectedPosition)).run\n      case ACTION_DRAG_ENDED =>\n        dragJobs.endAddItem().resolveAsync()\n        state = state.reset()\n        (this <~ select(unselectedPosition)).run\n      case _ =>\n    }\n\n  private[this] def updatePosition(\n      position: Int)(implicit theme: NineCardsTheme, uiContext: UiContext[_]): Transformer =\n    Transformer {\n      case view: TintableImageView if view.getPosition.contains(position) =>\n        view <~ populate(state.getDockApp(position))\n    }\n\n  private[this] def calculatePosition(x: Float): Option[Int] = {\n    val space = x.toInt / (getWidth / numberOfItems)\n    space match {\n      case `appDrawerPosition`        => None\n      case s if s < appDrawerPosition => Option(s)\n      case s                          => Some(s - 1)\n    }\n  }\n\n  private[this] def populate(dockApp: Option[DockAppData])(\n      implicit theme: NineCardsTheme,\n      uiContext: UiContext[_]): Tweak[TintableImageView] =\n    tivPressedColor(theme.get(DockPressedColor)) +\n      (dockApp map { app =>\n        (app.dockType match {\n          case AppDockType =>\n            ivSrcByPackageName(app.intent.extractPackageName(), app.name)\n          case ContactDockType =>\n            ivUriContactFromLookup(app.intent.extractLookup(), app.name, circular = true)\n          case _ => ivSrc(noFoundAppDrawable)\n        }) +\n          On.click(Ui {\n            navigationJobs.openDockApp(app).resolveServiceOr[Throwable] {\n              case e: LauncherExecutorProcessPermissionException =>\n                navigationJobs.openMomentIntentException(app.intent.extractPhone())\n              case _ => navigationJobs.navigationUiActions.showContactUsError()\n            }\n          })\n      } getOrElse ivSrc(noFoundAppDrawable) + On.click(Ui.nop))\n\n  private[this] def select(position: Int) = Transformer {\n    case view: TintableImageView if view.getPosition.contains(position) =>\n      view <~ applyAnimation(\n        scaleX = Option(selectedScale),\n        scaleY = Option(selectedScale),\n        alpha = Option(selectedAlpha))\n    case view: TintableImageView =>\n      view <~ applyAnimation(\n        scaleX = Option(defaultScale),\n        scaleY = Option(defaultScale),\n        alpha = Option(defaultAlpha))\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/EditDeviceMomentLayout.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.LinearLayout\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.commons._\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.{DrawerIconColor, DrawerTextColor}\nimport macroid.extras.TextViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nclass EditDeviceMomentLayout(context: Context, attrs: AttributeSet, defStyle: Int)\n    extends LinearLayout(context, attrs, defStyle)\n    with Contexts[View]\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attrs: AttributeSet) = this(context, attrs, 0)\n\n  lazy val name = findView(TR.edit_wifi_name)\n\n  lazy val deleteAction = findView(TR.edit_wifi_action_delete)\n\n  LayoutInflater.from(context).inflate(R.layout.edit_moment_wifi_layout, this)\n\n  def populate(deviceName: String, position: Int, onRemoveDevice: (Int => Unit))(\n      implicit theme: NineCardsTheme): Ui[Any] = {\n    val iconColor = theme.get(DrawerIconColor)\n    val textColor = theme.get(DrawerTextColor)\n    (this <~ vSetPosition(position)) ~\n      (name <~\n        tvText(deviceName) <~\n        tvColor(textColor)) ~\n      (deleteAction <~\n        tivDefaultColor(iconColor) <~\n        On.click(Ui(onRemoveDevice(position))))\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/EditHourMomentLayout.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.app.TimePickerDialog\nimport android.app.TimePickerDialog.OnTimeSetListener\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.{ImageView, LinearLayout, TimePicker}\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.ops.DrawableOps._\nimport cards.nine.app.ui.components.drawables.CharDrawable\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.commons.javaNull\nimport cards.nine.models.types.theme.{DrawerIconColor, DrawerTextColor}\nimport cards.nine.models.{MomentTimeSlot, NineCardsTheme}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nimport scala.util.Try\n\nclass EditHourMomentLayout(context: Context, attrs: AttributeSet, defStyle: Int)\n    extends LinearLayout(context, attrs, defStyle)\n    with Contexts[View]\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attrs: AttributeSet) = this(context, attrs, 0)\n\n  val margin = resGetDimensionPixelSize(R.dimen.padding_default)\n\n  val paddingLarge = resGetDimensionPixelSize(R.dimen.padding_large)\n\n  val daySelectedColor = resGetColor(R.color.collection_fab_button_item_1)\n\n  val dayUnselectedColor = resGetColor(R.color.edit_moment_unselected_day)\n\n  val daysWeek = getResources.getStringArray(R.array.days_letters).toList\n\n  lazy val startContent = findView(TR.edit_hour_start_content)\n\n  lazy val startText = findView(TR.edit_hour_start_text)\n\n  lazy val endContent = findView(TR.edit_hour_end_content)\n\n  lazy val endText = findView(TR.edit_hour_end_text)\n\n  lazy val deleteAction = findView(TR.edit_hour_action_delete)\n\n  lazy val daysContent = findView(TR.edit_hour_days_content)\n\n  LayoutInflater.from(context).inflate(R.layout.edit_moment_hour_layout, this)\n\n  def populate(\n      time: MomentTimeSlot,\n      position: Int,\n      onRemoveHour: (Int) => Unit,\n      onChangeFromHour: (Int, String) => Unit,\n      onChangeToHour: (Int, String) => Unit,\n      onSwapDays: (Int, Int) => Unit)(implicit theme: NineCardsTheme): Ui[Any] = {\n    val iconColor = theme.get(DrawerIconColor)\n    val arrow =\n      resGetDrawable(R.drawable.icon_edit_moment_arrow).colorize(iconColor)\n    val textColor = theme.get(DrawerTextColor)\n    (this <~ vSetPosition(position) <~ vGlobalLayoutListener(\n      _ => fillDays(position, time.days, onSwapDays))) ~\n      (startContent <~ On.click(showTime(position, time.from, from = true, onChangeFromHour))) ~\n      (endContent <~ On.click(showTime(position, time.to, from = false, onChangeToHour))) ~\n      (startText <~\n        tvText(time.from) <~\n        tvColor(textColor) <~\n        tvCompoundDrawablesWithIntrinsicBounds(right = Some(arrow))) ~\n      (endText <~\n        tvText(time.to) <~\n        tvColor(textColor) <~\n        tvCompoundDrawablesWithIntrinsicBounds(right = Some(arrow))) ~\n      (deleteAction <~\n        tivDefaultColor(iconColor) <~\n        On.click(Ui(onRemoveHour(position))))\n  }\n\n  private[this] def showTime(\n      position: Int,\n      time: String,\n      from: Boolean,\n      onChangeToHour: (Int, String) => Unit): Ui[Any] =\n    Try {\n      val timeArray = time.split(\":\")\n      (timeArray(0).toInt, timeArray(1).toInt)\n    }.toOption match {\n      case Some((hour, min)) =>\n        Ui {\n          val dialog = new TimePickerDialog(getContext, new OnTimeSetListener {\n            def timeToString(time: Int) =\n              if (time < 10) s\"0$time\" else time.toString\n            override def onTimeSet(view: TimePicker, hourOfDay: Int, minute: Int): Unit = {\n              val hour = s\"${timeToString(hourOfDay)}:${timeToString(minute)}\"\n              if (from)\n                onChangeToHour(position, hour)\n              else\n                onChangeToHour(position, hour)\n            }\n          }, hour, min, true)\n          dialog.show()\n        }\n      case _ => Ui.nop\n    }\n\n  private[this] def fillDays(position: Int, days: Seq[Int], onSwapDays: (Int, Int) => Unit) = {\n    val views = days.zipWithIndex map {\n      case (day, index) =>\n        val letter = daysWeek.lift(index) getOrElse \"\"\n        val color  = if (day == 0) dayUnselectedColor else daySelectedColor\n        (w[ImageView] <~\n          On.click(Ui(onSwapDays(position, index))) <~\n          ivSrc(CharDrawable(letter, circle = true, Some(color)))).get\n    }\n    val sizeDay = ((getWidth - (paddingLarge * 2)) / days.length) - (margin * 2)\n    val params  = new LinearLayout.LayoutParams(sizeDay, sizeDay)\n    params.setMargins(margin, margin, margin, margin)\n    daysContent <~\n      vgRemoveAllViews <~\n      vgAddViews(views, params)\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/FabItemMenu.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.OvalShape\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.{FrameLayout, ImageView, TextView}\nimport macroid.extras.DeviceVersion._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid._\n\nclass FabItemMenu(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends FrameLayout(context, attr, defStyleAttr)\n    with Contexts[View]\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  LayoutInflater.from(context).inflate(R.layout.fab_item, this)\n\n  val title = Option(findView(TR.fab_title))\n\n  val icon = Option(findView(TR.fab_icon))\n\n  (icon <~ fabStyle).run\n\n  def populate(backgroundColor: Int, res: Int, text: Int): Ui[Any] =\n    (title <~ tvText(text)) ~\n      (icon <~ ivSrc(res)) ~\n      changeBackground(backgroundColor)\n\n  def changeBackground(backgroundColor: Int): Ui[Any] =\n    icon <~\n      (Lollipop ifSupportedThen {\n        vBackgroundColor(backgroundColor)\n      } getOrElse {\n        val drawable = new ShapeDrawable(new OvalShape)\n        drawable.getPaint.setColor(backgroundColor)\n        vBackground(drawable)\n      })\n\n  private[this] def fabStyle: Tweak[ImageView] =\n    Lollipop ifSupportedThen {\n      vElevation(resGetDimension(R.dimen.elevation_fab_button)) + vCircleOutlineProvider()\n    } getOrElse Tweak.blank\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/FastScrollerLayout.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.graphics.Rect\nimport android.graphics.drawable.{Drawable, GradientDrawable}\nimport android.os.Build.VERSION._\nimport android.os.Build.VERSION_CODES._\nimport android.support.v4.view.MotionEventCompat\nimport android.support.v7.widget.RecyclerView._\nimport android.support.v7.widget.{LinearLayoutManager, RecyclerView}\nimport android.util.AttributeSet\nimport android.view.MotionEvent._\nimport android.view.ViewGroup.LayoutParams._\nimport android.view._\nimport android.widget.FrameLayout\nimport android.widget.FrameLayout.LayoutParams\nimport cards.nine.models.types.NineCardsCategory\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.commons._\nimport cards.nine.models.TermCounter\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nclass FastScrollerLayout(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends FrameLayout(context, attr, defStyleAttr) { self =>\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  lazy val fastScroller = new FastScrollerView(context)\n\n  override def onFinishInflate(): Unit = {\n    val ll = new LayoutParams(WRAP_CONTENT, MATCH_PARENT)\n    ll.gravity = Gravity.RIGHT\n    (this <~ vgAddView(fastScroller, ll)).run\n    super.onFinishInflate()\n  }\n\n  def linkRecycler(recyclerView: RecyclerView) =\n    (fastScroller <~ fsRecyclerView(recyclerView)).run\n\n  def setColor(color: Int, backgroundColor: Int) =\n    (fastScroller <~ fsColor(color) + fsBackgroundColor(backgroundColor)).run\n\n  def setMarginRightBarContent(pixels: Int) =\n    (fastScroller <~ fsMarginRightBarContent(pixels)).run\n\n  def setSignalType(signalType: FastScrollerSignalType) =\n    (fastScroller <~ fsSignalType(signalType)).run\n\n  def reset = (fastScroller <~ fsReset).run\n\n  def setCounters(counters: Seq[TermCounter]) =\n    (fastScroller <~ fsCounters(counters)).run\n\n  def setEnabledScroller(enabled: Boolean) =\n    (fastScroller <~\n      fsEnabledScroller(enabled) <~\n      (if (enabled) fsShow else fsHide)).run\n\n  private[this] def fsSignalType(signalType: FastScrollerSignalType) =\n    Tweak[FastScrollerView](_.setFastScrollerSignalType(signalType))\n\n  private[this] def fsReset = Tweak[FastScrollerView](_.reset())\n\n  private[this] def fsMarginRightBarContent(pixels: Int) =\n    Tweak[FastScrollerView](_.setBarContentMargin(pixels).run)\n\n  private[this] def fsCounters(counters: Seq[TermCounter]) =\n    Tweak[FastScrollerView](_.setCounters(counters))\n\n  private[this] def fsEnabledScroller(enabled: Boolean) =\n    Tweak[FastScrollerView](_.setEnabledScroller(enabled))\n\n  private[this] def fsRecyclerView(rv: RecyclerView) =\n    Tweak[FastScrollerView](view => view.setRecyclerView(rv))\n\n  private[this] def fsColor(color: Int) = Tweak[FastScrollerView] { view =>\n    ((view.bar <~ ivSrc(changeColor(R.drawable.fastscroller_bar, color))) ~\n      (view.signal <~ Tweak[FrameLayout](\n        _.setBackground(changeColor(R.drawable.fastscroller_signal, color))))).run\n  }\n\n  private[this] def fsBackgroundColor(color: Int) = Tweak[FastScrollerView] { view =>\n    (view.barContent <~ vBackgroundColor(color)).run\n  }\n\n  private[this] def fsShow = Tweak[FastScrollerView](_.show.run)\n\n  private[this] def fsHide = Tweak[FastScrollerView](_.hide.run)\n\n  private[this] def changeColor(res: Int, color: Int): Drawable =\n    getDrawable(res) match {\n      case drawable: GradientDrawable =>\n        drawable.setColor(color)\n        drawable\n      case drawable => drawable\n    }\n\n  private[this] def getDrawable(res: Int): Drawable =\n    if (SDK_INT < LOLLIPOP_MR1) {\n      context.getResources.getDrawable(res)\n    } else {\n      context.getResources.getDrawable(res, javaNull)\n    }\n\n}\n\nclass FastScrollerView(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends FrameLayout(context, attr, defStyleAttr)\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  private[this] var recyclerView = slot[RecyclerView]\n\n  private[this] var scrollListener: Option[ScrollListener] = None\n\n  var statuses = new FastScrollerStatuses\n\n  setClipChildren(false)\n\n  LayoutInflater.from(context).inflate(R.layout.fastscroller, this)\n\n  lazy val barContent = findView(TR.fastscroller_bar_content)\n\n  val bar = findView(TR.fastscroller_bar)\n\n  val signal = findView(TR.fastscroller_signal)\n\n  val text = findView(TR.fastscroller_signal_text)\n\n  val icon = findView(TR.fastscroller_signal_icon)\n\n  val barSize = context.getResources.getDimensionPixelOffset(R.dimen.fastscroller_bar_height)\n\n  override def onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int): Unit = {\n    super.onSizeChanged(w, h, oldw, oldh)\n    if (statuses.heightScroller != h) {\n      Option(getParent) match {\n        case Some(parent: View) =>\n          // Try to expand the touch event in the right side to improve the feedback to user\n          val delegateArea = new Rect()\n          getHitRect(delegateArea)\n          delegateArea.right = delegateArea.right + context.getResources.getDimensionPixelOffset(\n              R.dimen.padding_default)\n          parent.setTouchDelegate(new TouchDelegate(delegateArea, this) {\n            override def onTouchEvent(event: MotionEvent): Boolean =\n              touchEvent(event)\n          })\n        case _ =>\n      }\n\n      statuses = statuses.copy(heightScroller = h)\n      recyclerView foreach (rv =>\n                              statuses = statuses.resetRecyclerInfo(rv, statuses.heightScroller))\n      changePosition(0).run\n    }\n  }\n\n  override def onTouchEvent(event: MotionEvent): Boolean = touchEvent(event)\n\n  private[this] def touchEvent(event: MotionEvent): Boolean = {\n    val action = MotionEventCompat.getActionMasked(event)\n    val y      = flatInBoundaries(MotionEventCompat.getY(event, 0))\n    (statuses.enabled, action) match {\n      case (false, _) => super.onTouchEvent(event)\n      case (_, ACTION_DOWN) =>\n        statuses = statuses.startScroll()\n        true\n      case (_, ACTION_MOVE) =>\n        statuses = statuses.movingScroll()\n        (changePosition(y) ~\n          (if (statuses.usingCounters) showSignal else hideSignal) ~\n          (recyclerView <~ rvScrollToPosition(y))).run\n        true\n      case (_, ACTION_UP | ACTION_CANCEL) =>\n        statuses = statuses.resetScrollPosition()\n        // If the ScrollState of Recycler is SCROLL_STATE_SETTLING, we wait to resetScroll\n        // when the animation is finished using method onScrollStateChanged in OnScrollListener class\n        recyclerView foreach { rv =>\n          if (rv.getScrollState != SCROLL_STATE_SETTLING) {\n            statuses = statuses.resetScroll()\n          }\n        }\n        ((recyclerView <~ removeKeys() <~ rvInvalidateItemDecorations) ~ changePosition(y) ~ hideSignal).run\n        true\n      case _ => super.onTouchEvent(event)\n    }\n  }\n\n  private[this] def addKeys(position: Int, count: Int) =\n    vAddField(FastScrollerView.fastScrollerPositionKey, position) +\n      vAddField(FastScrollerView.fastScrollerCountKey, count)\n\n  private[this] def removeKeys() =\n    vRemoveField(FastScrollerView.fastScrollerPositionKey) +\n      vRemoveField(FastScrollerView.fastScrollerCountKey)\n\n  def setBarContentMargin(pixels: Int) =\n    barContent <~ vMargin(marginRight = pixels)\n\n  def show: Ui[_] = (signal <~ vGone) ~ (bar <~ vVisible)\n\n  def hide: Ui[_] = (signal <~ vGone) ~ (bar <~ vGone)\n\n  def showSignal: Ui[_] = signal <~ vVisible\n\n  def hideSignal: Ui[_] = signal <~ vGone\n\n  def setFastScrollerSignalType(signalType: FastScrollerSignalType): Unit = {\n    statuses = statuses.copy(fastScrollerSignalType = signalType)\n    signalType match {\n      case FastScrollerText     => ((icon <~ vGone) ~ (text <~ vVisible)).run\n      case FastScrollerCategory => ((icon <~ vVisible) ~ (text <~ vGone)).run\n      case FastScrollerInstallationDate =>\n        ((icon <~ vVisible) ~ (text <~ vGone)).run\n    }\n  }\n\n  def setRecyclerView(rv: RecyclerView): Unit = {\n    statuses = statuses.resetRecyclerInfo(rv, statuses.heightScroller)\n    scrollListener foreach rv.removeOnScrollListener\n    val sl = new ScrollListener\n    scrollListener = Option(sl)\n    rv.addOnScrollListener(sl)\n    recyclerView = Option(rv)\n  }\n\n  def reset(): Unit = {\n    recyclerView foreach (rv => statuses = statuses.resetRecyclerInfo(rv, statuses.heightScroller))\n    scrollListener foreach (_.reset())\n    changePosition(0).run\n  }\n\n  def setEnabledScroller(enabled: Boolean) =\n    statuses = statuses.copy(enabled = enabled)\n\n  def setCounters(counters: Seq[TermCounter]) =\n    statuses = statuses.copy(counters = counters)\n\n  private[this] def changePosition(y: Float): Ui[_] = {\n    val position   = y / statuses.heightScroller\n    val value      = ((statuses.heightScroller - barSize) * position).toInt\n    val max        = statuses.heightScroller - barSize\n    val minimum    = math.max(0, value)\n    val barPosY    = math.min(minimum, max)\n    val signalPosY = math.max(0, barPosY - barSize)\n    (bar <~ vY(barPosY)) ~ (signal <~ vY(signalPosY))\n  }\n\n  private[this] def rvScrollToPosition(y: Float) = Tweak[RecyclerView] { view =>\n    val position = getPosition(y)\n    if (position != statuses.lastScrollToPosition) {\n      statuses = statuses.copy(lastScrollToPosition = position)\n      if (statuses.usingCounters) {\n        val item       = statuses.counters(position)\n        val toPosition = (statuses.counters take position map (_.count)).sum\n        (updateSignal(item.term) ~\n          (recyclerView <~ addKeys(toPosition, item.count) <~ rvInvalidateItemDecorations)).run\n        view.smoothScrollToPosition(toPosition)\n      } else {\n        val toPosition = position * statuses.columns\n        view.smoothScrollToPosition(toPosition)\n      }\n    }\n  }\n\n  private[this] def updateSignal(term: String): Ui[_] =\n    statuses.fastScrollerSignalType match {\n      case FastScrollerText => text <~ tvText(term)\n      case FastScrollerCategory =>\n        icon <~\n          tvCompoundDrawablesWithIntrinsicBoundsResources(\n            top = getIconResource(NineCardsCategory(term).getIconResource)) <~\n          tvText(getStringResource(NineCardsCategory(term).getStringResource))\n      case FastScrollerInstallationDate =>\n        icon <~\n          tvCompoundDrawablesWithIntrinsicBoundsResources(\n            top = R.drawable.app_drawer_filter_installation_date) <~\n          tvText(getStringResource(term))\n    }\n\n  private[this] def getIconResource(name: String) = {\n    val resourceName = s\"icon_collection_${name}_detail\"\n    val resource     = getResources.getIdentifier(resourceName, \"drawable\", context.getPackageName)\n    if (resource == 0) R.drawable.icon_collection_default_detail else resource\n  }\n\n  private[this] def getStringResource(name: String) = {\n    val resource =\n      getResources.getIdentifier(name, \"string\", context.getPackageName)\n    if (resource == 0) R.string.app_name else resource\n  }\n\n  private[this] def getPosition(y: Float) =\n    if (statuses.usingCounters) {\n      ((y * statuses.counters.length) / statuses.heightScroller).toInt\n    } else {\n      ((y * statuses.rows) / statuses.heightScroller).toInt\n    }\n\n  private[this] def getRowFirstItem(recyclerView: RecyclerView): Int = {\n    // Update child count if it's necessary\n    if (statuses.visibleRows == 0) {\n      val visibleRows = statuses.childCountToRows(recyclerView.getChildCount)\n      statuses = statuses.copy(visibleRows = visibleRows)\n    }\n\n    val firstVisiblePosition = recyclerView.getLayoutManager match {\n      case lm: LinearLayoutManager => lm.findFirstVisibleItemPosition()\n      case _                       => 0\n    }\n\n    statuses.childCountToRows(firstVisiblePosition)\n  }\n\n  private[this] def flatInBoundaries(y: Float) = y match {\n    case v if v < 0                       => 0f\n    case v if v > statuses.heightScroller => statuses.heightScroller.toFloat\n    case v                                => v\n  }\n\n  class ScrollListener extends OnScrollListener {\n\n    private[this] var lastRowFirstItem = 0\n\n    private[this] var offsetY = 0f\n\n    private[this] var oldState = SCROLL_STATE_IDLE\n\n    override def onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int): Unit =\n      if (!statuses.moving) {\n        val rowFirstItem = getRowFirstItem(recyclerView)\n        val y            = statuses.projectToBar(rowFirstItem)\n        val maxRows      = statuses.maxRows\n        val move = if (rowFirstItem == lastRowFirstItem && maxRows > 0) {\n          // We calculate the displacement between the last and current row\n          offsetY = offsetY + dy\n          val ratio = offsetY / statuses.heightRow.toFloat\n          val space = statuses.heightScroller / maxRows\n          y + (ratio * space)\n        } else {\n          lastRowFirstItem = rowFirstItem\n          offsetY = 0\n          y\n        }\n        changePosition(move).run\n      } else if (recyclerView.getScrollState != statuses.scrollState) {\n        statuses = statuses.copy(scrollState = recyclerView.getScrollState)\n      }\n\n    override def onScrollStateChanged(recyclerView: RecyclerView, newState: Int): Unit = {\n      if (statuses.moving && oldState == SCROLL_STATE_SETTLING && newState == SCROLL_STATE_IDLE) {\n        statuses = statuses.resetScroll()\n      }\n      oldState = newState\n      super.onScrollStateChanged(recyclerView, newState)\n    }\n\n    def reset(): Unit = {\n      lastRowFirstItem = 0\n      offsetY = 0f\n    }\n\n  }\n\n}\n\nobject FastScrollerView {\n  val fastScrollerPositionKey = \"position\"\n  val fastScrollerCountKey    = \"count\"\n}\n\ncase class FastScrollerStatuses(\n    enabled: Boolean = true,\n    heightScroller: Int = 0,\n    heightAllRows: Int = 0,\n    heightRow: Int = 0,\n    columns: Int = 0,\n    moving: Boolean = false,\n    totalItems: Int = 0,\n    visibleRows: Int = 0,\n    scrollState: Int = SCROLL_STATE_IDLE,\n    lastScrollToPosition: Int = -1,\n    fastScrollerSignalType: FastScrollerSignalType = FastScrollerText,\n    counters: Seq[TermCounter] = Seq.empty) {\n\n  /**\n   * Update information related to data from recyclerview\n   */\n  def resetRecyclerInfo(recyclerView: RecyclerView, height: Int): FastScrollerStatuses = {\n    val (allRows, item, columns) = Option(recyclerView.getAdapter) match {\n      case Some(listener: FastScrollerListener) =>\n        (listener.getHeightAllRows - height, listener.getHeightItem, listener.getColumns)\n      case _ => (0, 0, 0)\n    }\n    val total = Option(recyclerView.getAdapter) match {\n      case Some(adapter) => adapter.getItemCount\n      case _             => 0\n    }\n    copy(\n      heightAllRows = allRows,\n      heightRow = item,\n      totalItems = total,\n      columns = columns,\n      visibleRows = 0,\n      moving = false)\n  }\n\n  // Number of rows of recyclerview given the number of columns\n  def rows: Int = math.ceil(totalItems.toFloat / columns).toInt\n\n  // Maximum number of rows for calculate the position of bar in fastscroller\n  def maxRows: Int = rows - visibleRows\n\n  // Number of rows for a number of items\n  def childCountToRows(count: Int) = math.ceil(count.toFloat / columns).toInt\n\n  // Calculate y position given first rom position\n  def projectToBar(rowFirstItem: Int) =\n    heightScroller * (rowFirstItem.toFloat / maxRows.toFloat)\n\n  def startScroll(): FastScrollerStatuses = copy(moving = true)\n\n  def movingScroll(): FastScrollerStatuses = copy(moving = true)\n\n  def resetScrollPosition(): FastScrollerStatuses =\n    copy(lastScrollToPosition = -1)\n\n  def resetScroll(): FastScrollerStatuses =\n    copy(moving = false, scrollState = SCROLL_STATE_IDLE)\n\n  def usingCounters = counters.nonEmpty\n\n}\n\ntrait FastScrollerListener {\n\n  def getHeightAllRows: Int\n\n  def getHeightItem: Int\n\n  def getColumns: Int\n\n}\n\nsealed trait FastScrollerSignalType\n\ncase object FastScrollerCategory extends FastScrollerSignalType\n\ncase object FastScrollerInstallationDate extends FastScrollerSignalType\n\ncase object FastScrollerText extends FastScrollerSignalType\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/LauncherWorkSpaces.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.app.WallpaperManager\nimport android.appwidget.AppWidgetHostView\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.MotionEvent\nimport android.view.MotionEvent._\nimport android.widget.FrameLayout\nimport cards.nine.app.ui.commons.ops.WidgetsOps.Cell\nimport cards.nine.app.ui.components.commons.TranslationAnimator\nimport cards.nine.app.ui.components.models.{\n  CollectionsWorkSpace,\n  LauncherData,\n  MomentWorkSpace,\n  WorkSpaceType\n}\nimport cards.nine.app.ui.launcher.LauncherActivity\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.holders.{\n  LauncherWorkSpaceCollectionsHolder,\n  LauncherWorkSpaceMomentsHolder\n}\nimport cards.nine.app.ui.launcher.jobs.{DragJobs, NavigationJobs, WidgetsJobs}\nimport cards.nine.app.ui.preferences.commons.{\n  AppearBehindWorkspaceAnimation,\n  HorizontalSlideWorkspaceAnimation,\n  WallpaperAnimation\n}\nimport cards.nine.commons.javaNull\nimport cards.nine.models.{Collection, NineCardsTheme, Widget}\nimport macroid._\nimport macroid.extras.UIActionsExtras._\nimport macroid.extras.ViewTweaks._\n\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.Future\nimport scala.language.postfixOps\n\nclass LauncherWorkSpaces(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends AnimatedWorkSpaces[LauncherWorkSpaceHolder, LauncherData](context, attr, defStyleAttr) {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  implicit def navigationJobs: NavigationJobs = context match {\n    case activity: LauncherActivity => activity.navigationJobs\n    case _                          => throw new RuntimeException(\"NavigationJobs not found\")\n  }\n\n  implicit def dragJobs: DragJobs = context match {\n    case activity: LauncherActivity => activity.dragJobs\n    case _                          => throw new RuntimeException(\"DragJobs not found\")\n  }\n\n  implicit def widgetJobs: WidgetsJobs = context match {\n    case activity: LauncherActivity => activity.widgetJobs\n    case _                          => throw new RuntimeException(\"WidgetsJobs not found\")\n  }\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  lazy val canAnimateWallpaper = WallpaperAnimation.readValue\n\n  lazy val wallpaperManager: WallpaperManager =\n    WallpaperManager.getInstance(context)\n\n  lazy val windowToken = getWindowToken\n\n  var workSpacesStatuses = LauncherWorkSpacesStatuses()\n\n  var workSpacesListener = LauncherWorkSpacesListener()\n\n  val menuAnimator = new TranslationAnimator(update = (value: Float) => {\n    workSpacesStatuses = workSpacesStatuses.copy(displacement = value)\n    updateCanvasMenu()\n  })\n\n  lazy val sizeCalculateMovement = getHeight\n\n  override def init(\n      newData: Seq[LauncherData],\n      position: Int = 0,\n      forcePopulatePosition: Option[Int] = None): Unit = {\n    super.init(newData, position, forcePopulatePosition)\n    updateWallpaper().run\n  }\n\n  def reloadMoment(moment: LauncherData): Unit = {\n    data = moment +: data.drop(1)\n    resetItem(0).run\n  }\n\n  def getCountCollections: Int =\n    data map {\n      case item @ LauncherData(CollectionsWorkSpace, _, _, _) =>\n        item.collections.length\n      case _ => 0\n    } sum\n\n  def getCollections: Seq[Collection] = data flatMap (a => a.collections)\n\n  def isEmptyCollections: Boolean = getCountCollections == 0\n\n  def isMomentWorkSpace: Boolean =\n    data(animatedWorkspaceStatuses.currentItem).workSpaceType.isMomentWorkSpace\n\n  def isMomentWorkSpace(page: Int): Boolean =\n    data(page).workSpaceType.isMomentWorkSpace\n\n  def isCollectionWorkSpace: Boolean = !isMomentWorkSpace\n\n  def isCollectionWorkSpace(page: Int): Boolean = !isMomentWorkSpace(page)\n\n  def getWidgets: Seq[Widget] = getView(0) match {\n    case (Some(momentWorkSpace: LauncherWorkSpaceMomentsHolder)) =>\n      momentWorkSpace.getWidgets\n    case _ => Seq.empty\n  }\n\n  def changeCollectionInMoment(collection: Option[Collection]): Unit = {\n    data.headOption match {\n      case Some(momentLauncherData) =>\n        val collectionsLauncherData =\n          data.filter(_.workSpaceType == CollectionsWorkSpace)\n        val newMoment = momentLauncherData.moment map (_.copy(collection = collection))\n        data = momentLauncherData.copy(moment = newMoment) +: collectionsLauncherData\n      case _ =>\n    }\n  }\n\n  def nextScreen: Option[Int] = {\n    val current = animatedWorkspaceStatuses.currentItem\n    if (current + 1 < getWorksSpacesCount) Some(current + 1) else None\n  }\n\n  def previousScreen: Option[Int] = {\n    val current = animatedWorkspaceStatuses.currentItem\n    if (current > 0) Some(current - 1) else None\n  }\n\n  def prepareItemsScreenInReorder(position: Int): Ui[Any] =\n    getCurrentView match {\n      case Some(collectionWorkspace: LauncherWorkSpaceCollectionsHolder) =>\n        collectionWorkspace.prepareItemsScreenInReorder(position)\n      case _ => Ui.nop\n    }\n\n  def addWidget(widgetView: AppWidgetHostView, cell: Cell, widget: Widget): Unit =\n    getView(0) match {\n      case (Some(momentWorkSpace: LauncherWorkSpaceMomentsHolder)) =>\n        momentWorkSpace.addWidget(widgetView, cell, widget).run\n      case None =>\n        // The first time it`s possible that the workspace isn't created. In this case we wait 200 millis for launching again\n        uiHandlerDelayed(Ui(addWidget(widgetView, cell, widget)), 200).run\n      case _ =>\n    }\n\n  def addNoConfiguredWidget(wCell: Int, hCell: Int, widget: Widget): Unit =\n    getView(0) match {\n      case (Some(momentWorkSpace: LauncherWorkSpaceMomentsHolder)) =>\n        momentWorkSpace.addNoConfiguredWidget(wCell, hCell, widget).run\n      case None =>\n        // The first time it`s possible that the workspace isn't created. In this case we wait 200 millis for launching again\n        uiHandlerDelayed(Ui(addNoConfiguredWidget(wCell, hCell, widget)), 200).run\n      case _ =>\n    }\n\n  def addReplaceWidget(\n      widgetView: AppWidgetHostView,\n      wCell: Int,\n      hCell: Int,\n      widget: Widget): Unit = getView(0) match {\n    case (Some(momentWorkSpace: LauncherWorkSpaceMomentsHolder)) =>\n      momentWorkSpace.addReplaceWidget(widgetView, wCell, hCell, widget).run\n    case _ =>\n  }\n\n  def clearWidgets(): Unit = uiWithView(_.clearWidgets)\n\n  def unhostWidget(id: Int): Unit = uiWithView(_.unhostWiget(id))\n\n  def startEditWidget(): Unit = uiWithView(_.startEditWidget())\n\n  def closeEditWidget(): Unit = uiWithView(_.closeEditWidget())\n\n  def reloadSelectedWidget(): Unit = uiWithView(_.reloadSelectedWidget)\n\n  private[this] def uiWithView(f: (LauncherWorkSpaceMomentsHolder) => Ui[Any]) = getView(0) match {\n    case (Some(momentWorkSpace: LauncherWorkSpaceMomentsHolder)) =>\n      f(momentWorkSpace).run\n    case _ =>\n  }\n\n  def openMenu(): Unit = {\n    workSpacesStatuses = workSpacesStatuses.startLaunchedOpen()\n    (uiVibrate() ~\n      workSpacesListener.onStartOpenMenu() ~\n      (this <~\n        vInvalidate <~~\n        menuAnimator.move(0, -sizeCalculateMovement / 2)) ~~\n      resetMenuMovement()).run\n  }\n\n  override def getItemViewTypeCount: Int = 2\n\n  override def getItemViewType(data: LauncherData, position: Int): Int =\n    data.workSpaceType.value\n\n  override def createEmptyView(): LauncherWorkSpaceHolder =\n    new LauncherWorkSpaceHolder(context)\n\n  override def createView(viewType: Int): LauncherWorkSpaceHolder =\n    WorkSpaceType(viewType) match {\n      case MomentWorkSpace =>\n        new LauncherWorkSpaceMomentsHolder(context, animatedWorkspaceStatuses.dimen)\n      case CollectionsWorkSpace =>\n        new LauncherWorkSpaceCollectionsHolder(context, animatedWorkspaceStatuses.dimen)\n    }\n\n  override def populateView(\n      view: Option[LauncherWorkSpaceHolder],\n      data: LauncherData,\n      viewType: Int,\n      position: Int): Ui[Any] =\n    view match {\n      case Some(v: LauncherWorkSpaceCollectionsHolder) =>\n        v.populate(data.collections, data.positionByType)\n      case Some(v: LauncherWorkSpaceMomentsHolder) =>\n        data.moment map v.populate getOrElse Ui.nop\n      case _ => Ui.nop\n    }\n\n  override def onInterceptTouchEvent(event: MotionEvent): Boolean = {\n    val (action, x, y) = updateTouch(event)\n    if (workSpacesStatuses.openingMenu) {\n      action match {\n        case ACTION_MOVE =>\n          requestDisallowInterceptTouchEvent(true)\n          val deltaY = animatedWorkspaceStatuses.deltaY(y)\n          animatedWorkspaceStatuses =\n            animatedWorkspaceStatuses.copy(lastMotionX = x, lastMotionY = y)\n          performMenuMovement(deltaY).run\n        case ACTION_DOWN =>\n          animatedWorkspaceStatuses =\n            animatedWorkspaceStatuses.copy(lastMotionX = x, lastMotionY = y)\n        case ACTION_CANCEL | ACTION_UP =>\n          computeFlingMenuMovement()\n        case _ =>\n      }\n      true\n    } else {\n      checkResetMenuOpened(action, x, y)\n      super.onInterceptTouchEvent(event)\n    }\n  }\n\n  override def onTouchEvent(event: MotionEvent): Boolean = {\n    val (action, x, y) = updateTouch(event)\n    if (workSpacesStatuses.openingMenu) {\n      action match {\n        case ACTION_MOVE =>\n          requestDisallowInterceptTouchEvent(true)\n          val deltaY = animatedWorkspaceStatuses.deltaY(y)\n          animatedWorkspaceStatuses =\n            animatedWorkspaceStatuses.copy(lastMotionX = x, lastMotionY = y)\n          performMenuMovement(deltaY).run\n        case ACTION_DOWN =>\n          animatedWorkspaceStatuses =\n            animatedWorkspaceStatuses.copy(lastMotionX = x, lastMotionY = y)\n        case ACTION_CANCEL | ACTION_UP =>\n          computeFlingMenuMovement()\n        case _ =>\n      }\n      true\n    } else {\n      checkResetMenuOpened(action, x, y)\n      super.onTouchEvent(event)\n    }\n  }\n\n  override def setStateIfNeeded(x: Float, y: Float): Unit = {\n    val touchingWidget = statuses.touchingWidget\n    // We check that the user is doing up vertical swipe\n    // If the user is touching a widget, we don't do a vertical movement in order to the\n    // scrollable widgets works fine\n    if (isVerticalMoving(x, y) && !touchingWidget && animatedWorkspaceStatuses.enabled) {\n      workSpacesListener.onStartOpenMenu().run\n      workSpacesStatuses = workSpacesStatuses.start()\n    } else {\n      super.setStateIfNeeded(x, y)\n    }\n  }\n\n  override def applyTransforms(): Ui[Any] =\n    updateWallpaper() ~ transformOutPanel() ~ transformInPanel()\n\n  def closeMenu(): Ui[Future[Any]] =\n    if (workSpacesStatuses.openedMenu) {\n      setOpenedMenu(false)\n      animateViewsMenuMovement(0, durationAnimation)\n    } else Ui(Future.successful(()))\n\n  private[this] def transformOutPanel(): Ui[Any] = {\n    val percent = animatedWorkspaceStatuses.percent(getSizeWidget)\n    getFrontView <~ ((animationPref, animatedWorkspaceStatuses.isFromLeft) match {\n      case (HorizontalSlideWorkspaceAnimation, _) =>\n        vTranslationX(animatedWorkspaceStatuses.displacement)\n      case (AppearBehindWorkspaceAnimation, true) =>\n        val alpha = 1 - percent\n        val scale = .5f + (alpha / 2)\n        vScaleX(scale) + vScaleY(scale) + vAlpha(alpha)\n      case (AppearBehindWorkspaceAnimation, false) =>\n        vTranslationX(animatedWorkspaceStatuses.displacement)\n    })\n  }\n\n  private[this] def transformInPanel(): Ui[Any] = {\n    val percent  = animatedWorkspaceStatuses.percent(getSizeWidget)\n    val fromLeft = animatedWorkspaceStatuses.isFromLeft\n    val view     = if (fromLeft) getPreviousView else getNextView\n    notifyMovementObservers(percent)\n\n    view <~ ((animationPref, fromLeft) match {\n      case (HorizontalSlideWorkspaceAnimation, _) =>\n        val translate = {\n          val start = if (fromLeft) -getSizeWidget else getSizeWidget\n          start - (start * percent)\n        }\n        vTranslationX(translate)\n      case (AppearBehindWorkspaceAnimation, true) =>\n        val translate = {\n          val start = -getSizeWidget\n          start - (start * percent)\n        }\n        vTranslationX(translate)\n      case (AppearBehindWorkspaceAnimation, false) =>\n        val scale = .5f + (percent / 2)\n        vTranslationX(0) + vScaleX(scale) + vScaleY(scale) + vAlpha(percent)\n    })\n\n  }\n\n  private[this] def updateWallpaper(): Ui[Any] =\n    if (canAnimateWallpaper) Ui {\n      wallpaperManager.setWallpaperOffsets(\n        windowToken,\n        animatedWorkspaceStatuses.totalXPercent(getSizeWidget, data.length),\n        0.5f)\n    } else Ui.nop\n\n  private[this] def checkResetMenuOpened(action: Int, x: Float, y: Float) = {\n    action match {\n      case ACTION_DOWN =>\n        statuses = statuses.copy(touchingWidget = false)\n        animatedWorkspaceStatuses =\n          animatedWorkspaceStatuses.copy(lastMotionX = x, lastMotionY = y)\n      case _ =>\n    }\n  }\n\n  private[this] def isVerticalMoving(x: Float, y: Float): Boolean = {\n    val xDiff = math.abs(x - animatedWorkspaceStatuses.lastMotionX)\n    val yDiff = math.abs(y - animatedWorkspaceStatuses.lastMotionY)\n\n    val rightDirection =\n      (workSpacesStatuses.openedMenu, y - animatedWorkspaceStatuses.lastMotionY < 0) match {\n        case (false, true) => true\n        case (true, false) => true\n        case _             => false\n      }\n\n    val yMoved = yDiff > touchSlop\n    yMoved && rightDirection && (yDiff > xDiff)\n  }\n\n  private[this] def performMenuMovement(delta: Float): Ui[Any] = {\n    menuAnimator.cancel()\n    workSpacesStatuses = workSpacesStatuses.updateDisplacement(sizeCalculateMovement, delta)\n    updateCanvasMenu()\n  }\n\n  private[this] def updateCanvasMenu(): Ui[Any] = {\n    val percent       = workSpacesStatuses.percent(sizeCalculateMovement)\n    val updatePercent = 1 - workSpacesStatuses.percent(sizeCalculateMovement)\n    val transform     = workSpacesStatuses.displacement < 0 && updatePercent > .5f\n    if (transform) {\n      workSpacesListener.onUpdateOpenMenu(percent * 2) ~\n        (getFrontView <~ vScaleX(updatePercent) <~ vScaleY(updatePercent) <~ vAlpha(updatePercent))\n    } else {\n      Ui.nop\n    }\n  }\n\n  private[this] def resetMenuMovement(): Ui[Any] = {\n    workSpacesStatuses = workSpacesStatuses.reset()\n    workSpacesListener.onEndOpenMenu(workSpacesStatuses.openedMenu)\n  }\n\n  private[this] def animateViewsMenuMovement(dest: Int, duration: Int): Ui[Future[Any]] =\n    (this <~\n      vInvalidate <~~\n      menuAnimator.move(workSpacesStatuses.displacement, dest, duration)) ~~ resetMenuMovement()\n\n  private[this] def snapMenuMovement(velocity: Float): Ui[Any] = {\n    moveItemsAnimator.cancel()\n    val destiny = (velocity, workSpacesStatuses.displacement) match {\n      case (v, d) if v <= 0 && d < 0 =>\n        setOpenedMenu(true)\n        -sizeCalculateMovement / 2\n      case _ =>\n        setOpenedMenu(false)\n        0\n    }\n    animateViewsMenuMovement(destiny, calculateDurationByVelocity(velocity, durationAnimation))\n  }\n\n  private[this] def snapDestinationMenuMovement(): Ui[Any] = {\n    val destiny = workSpacesStatuses.percent(sizeCalculateMovement) match {\n      case d if d > .25f =>\n        setOpenedMenu(true)\n        -sizeCalculateMovement / 2\n      case _ =>\n        setOpenedMenu(false)\n        0\n    }\n    animateViewsMenuMovement(destiny, durationAnimation)\n  }\n\n  private[this] def computeFlingMenuMovement() =\n    animatedWorkspaceStatuses.velocityTracker foreach { tracker =>\n      tracker.computeCurrentVelocity(1000, maximumVelocity)\n      (if (math.abs(tracker.getYVelocity) > minimumVelocity)\n         snapMenuMovement(tracker.getYVelocity)\n       else\n         snapDestinationMenuMovement()).run\n      tracker.recycle()\n      animatedWorkspaceStatuses = animatedWorkspaceStatuses.copy(velocityTracker = None)\n    }\n\n  private[this] def setOpenedMenu(openedMenu: Boolean): Unit = {\n    workSpacesStatuses = workSpacesStatuses.copy(openedMenu = openedMenu)\n    animatedWorkspaceStatuses = animatedWorkspaceStatuses.copy(enabled = !openedMenu)\n  }\n\n}\n\ncase class LauncherWorkSpacesStatuses(\n    openingMenu: Boolean = false,\n    openedMenu: Boolean = false,\n    displacement: Float = 0) {\n\n  def updateDisplacement(size: Int, delta: Float): LauncherWorkSpacesStatuses =\n    copy(displacement = math.max(-size, Math.min(size, displacement - delta)))\n\n  def percent(size: Int): Float = math.abs(displacement) / size\n\n  def start(): LauncherWorkSpacesStatuses = copy(openingMenu = true)\n\n  def startLaunchedOpen(): LauncherWorkSpacesStatuses = copy(openedMenu = true)\n\n  def reset(): LauncherWorkSpacesStatuses = copy(openingMenu = false)\n\n}\n\ncase class LauncherWorkSpacesListener(\n    onStartOpenMenu: () => Ui[Any] = () => Ui.nop,\n    onUpdateOpenMenu: (Float) => Ui[Any] = (f) => Ui.nop,\n    onEndOpenMenu: (Boolean) => Ui[Any] = (b) => Ui.nop)\n\nclass LauncherWorkSpaceHolder(context: Context) extends FrameLayout(context)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/PullToCloseView.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport cards.nine.commons._\nimport macroid.ContextWrapper\n\nclass PullToCloseView(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends PullToDownView(context, attr, defStyleAttr) {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  var closeListeners = PullToCloseListener()\n\n  override def drop(): Unit =\n    if (pullToDownStatuses.isValidAction) {\n      closeListeners.close()\n    } else {\n      super.drop()\n    }\n\n}\n\ncase class PullToCloseListener(close: () => Unit = () => ())\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/PullToDownView.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.animation.ValueAnimator.AnimatorUpdateListener\nimport android.animation.{Animator, AnimatorListenerAdapter, ValueAnimator}\nimport android.content.Context\nimport android.support.v4.view.{MotionEventCompat, ViewConfigurationCompat}\nimport android.util.AttributeSet\nimport android.view.MotionEvent._\nimport android.view.ViewGroup.{LayoutParams, MarginLayoutParams}\nimport android.view._\nimport macroid.extras.ResourcesExtras._\nimport cards.nine.app.ui.components.commons.{SwipeController, Swiping}\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.Contexts\n\nclass PullToDownView(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends ViewGroup(context, attr, defStyleAttr)\n    with Contexts[View]\n    with SwipeController {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  lazy val content = getChildAt(0)\n\n  var pullToDownStatuses = PullToDownStatuses(\n    distanceToValidAction = resGetDimensionPixelSize(R.dimen.distance_to_valid_action))\n\n  var pullingListeners = PullingListener()\n\n  var horizontalListener = HorizontalMovementListener()\n\n  val touchSlop = {\n    val configuration: ViewConfiguration = ViewConfiguration.get(getContext)\n    ViewConfigurationCompat.getScaledPagingTouchSlop(configuration)\n  }\n\n  override def checkLayoutParams(p: ViewGroup.LayoutParams): Boolean =\n    p.isInstanceOf[MarginLayoutParams]\n\n  override def generateDefaultLayoutParams(): ViewGroup.LayoutParams =\n    new MarginLayoutParams(\n      ViewGroup.LayoutParams.MATCH_PARENT,\n      ViewGroup.LayoutParams.MATCH_PARENT)\n\n  override def generateLayoutParams(p: LayoutParams): LayoutParams =\n    new MarginLayoutParams(p)\n\n  override def generateLayoutParams(attrs: AttributeSet): ViewGroup.LayoutParams =\n    new MarginLayoutParams(getContext, attrs)\n\n  override def onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int): Unit = {\n    super.onMeasure(widthMeasureSpec, heightMeasureSpec)\n    content.getLayoutParams match {\n      case lp: ViewGroup.MarginLayoutParams =>\n        val childWidthMeasureSpec = ViewGroup.getChildMeasureSpec(\n          widthMeasureSpec,\n          getPaddingLeft + getPaddingRight + lp.leftMargin + lp.rightMargin,\n          lp.width)\n        val childHeightMeasureSpec = ViewGroup.getChildMeasureSpec(\n          heightMeasureSpec,\n          getPaddingTop + getPaddingBottom + lp.topMargin,\n          lp.height)\n        content.measure(childWidthMeasureSpec, childHeightMeasureSpec)\n      case _ =>\n    }\n  }\n\n  override def onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int): Unit =\n    content.getLayoutParams match {\n      case lp: ViewGroup.MarginLayoutParams =>\n        val left: Int   = getPaddingLeft + lp.leftMargin\n        val top: Int    = getPaddingTop + lp.topMargin + pullToDownStatuses.currentPosY\n        val right: Int  = left + content.getMeasuredWidth\n        val bottom: Int = top + content.getMeasuredHeight\n        content.layout(left, top, right, bottom)\n      case _ =>\n    }\n\n  override def dispatchTouchEvent(event: MotionEvent): Boolean = {\n    val x      = MotionEventCompat.getX(event, 0)\n    val y      = MotionEventCompat.getY(event, 0)\n    val action = MotionEventCompat.getActionMasked(event)\n    updateSwipe(event)\n    (pullToDownStatuses.action, action) match {\n      case (_, ACTION_DOWN)                     => actionDown(event, x, y)\n      case (NoMovement, ACTION_MOVE)            => actionMoveIdle(event, x, y)\n      case (Pulling, ACTION_MOVE)               => actionMovePulling(event, x, y)\n      case (Pulling, ACTION_UP | ACTION_CANCEL) => releasePulling(event)\n      case (HorizontalMovement, ACTION_MOVE) =>\n        actionMoveHorizontal(event, x, y)\n      case (HorizontalMovement, ACTION_UP | ACTION_CANCEL) =>\n        releaseHorizontal(event)\n      case _ => super.dispatchTouchEvent(event)\n    }\n  }\n\n  def drop(): Unit = {\n    val anim: ValueAnimator = ValueAnimator.ofInt(0, 100)\n    anim.addUpdateListener(new AnimatorUpdateListener {\n      override def onAnimationUpdate(animation: ValueAnimator): Unit = {\n        movePos(-pullToDownStatuses.currentPosY * animation.getAnimatedFraction)\n        requestDisallowInterceptTouchEvent(true)\n      }\n    })\n    anim.addListener(new AnimatorListenerAdapter {\n      override def onAnimationEnd(animation: Animator): Unit = {\n        pullingListeners.end()\n        pullToDownStatuses = pullToDownStatuses.copy(action = NoMovement)\n        restart()\n      }\n    })\n    anim.start()\n  }\n\n  private[this] def actionDown(ev: MotionEvent, x: Float, y: Float): Boolean = {\n    pullToDownStatuses = pullToDownStatuses.start(x, y, NoMovement)\n    super.dispatchTouchEvent(ev)\n    true\n  }\n\n  private[this] def actionMoveIdle(ev: MotionEvent, x: Float, y: Float): Boolean = {\n    if (pullToDownStatuses.enabled) {\n      val deltaX  = x - pullToDownStatuses.startX\n      val deltaY  = y - pullToDownStatuses.startY\n      val pulling = childInTop && (deltaY > touchSlop)\n      val moveX = pullToDownStatuses.scrollHorizontalEnabled &&\n          (math.abs(deltaX) > touchSlop) &&\n          (math.abs(deltaX) > math.abs(deltaY))\n      (pulling, moveX) match {\n        case (true, _) =>\n          pullToDownStatuses = pullToDownStatuses.start(x, y, Pulling)\n          pullingListeners.start()\n        case (_, true) =>\n          pullToDownStatuses = pullToDownStatuses.start(x, y, HorizontalMovement)\n          horizontalListener.start()\n        case _ =>\n      }\n    }\n    super.dispatchTouchEvent(ev)\n  }\n\n  private[this] def actionMovePulling(ev: MotionEvent, x: Float, y: Float): Boolean = {\n    pullToDownStatuses = pullToDownStatuses.move(x, y)\n    val moveDown = pullToDownStatuses.offsetY > 0\n\n    (moveDown, !moveDown, pullToDownStatuses.hasLeftStartPosition, childInTop) match {\n      case (down, _, _, inTop) if down && !inTop => // disable move when user not reach top\n      case (down, up, canUp, _) if (up && canUp) || down =>\n        movePos(pullToDownStatuses.offsetY)\n      case _ =>\n    }\n    super.dispatchTouchEvent(ev)\n  }\n\n  private[this] def releasePulling(ev: MotionEvent): Boolean = {\n    recycleSwipe()\n    if (pullToDownStatuses.currentPosY > 0) {\n      drop()\n    } else {\n      pullingListeners.end()\n      pullToDownStatuses = pullToDownStatuses.copy(action = NoMovement)\n    }\n    super.dispatchTouchEvent(ev)\n  }\n\n  private[this] def actionMoveHorizontal(ev: MotionEvent, x: Float, y: Float): Boolean = {\n    requestDisallowInterceptTouchEvent(true)\n    val to = pullToDownStatuses.currentPosY + pullToDownStatuses.offsetX.toInt\n    pullToDownStatuses = pullToDownStatuses.updateCurrentPostX(to)\n    horizontalListener.scroll(pullToDownStatuses.offsetX.toInt)\n    pullToDownStatuses = pullToDownStatuses.move(x, y)\n    super.dispatchTouchEvent(ev)\n  }\n\n  private[this] def releaseHorizontal(ev: MotionEvent): Boolean = {\n    val swipe = currentSwiping\n    recycleSwipe()\n    horizontalListener.end(swipe, -pullToDownStatuses.currentPosX)\n    pullToDownStatuses = pullToDownStatuses.restart()\n    super.dispatchTouchEvent(ev)\n  }\n\n  private[this] def restart() = {\n    content.offsetTopAndBottom(-pullToDownStatuses.currentPosY)\n    invalidate()\n    pullToDownStatuses = pullToDownStatuses.restart()\n  }\n\n  private[this] def movePos(deltaY: Float) = {\n    if (deltaY >= 0 || !pullToDownStatuses.isInStartPosition) {\n      val to: Int = {\n        val to = pullToDownStatuses.currentPosY + deltaY.toInt\n        pullToDownStatuses.willOverTop(to) match {\n          case true  => pullToDownStatuses.posStart\n          case false => to\n        }\n      }\n      pullToDownStatuses = pullToDownStatuses.updateCurrentPostY(to)\n      pullingListeners.scroll(to, pullToDownStatuses.isValidAction)\n      val change: Int = to - pullToDownStatuses.lastPosY\n      updatePos(change)\n    }\n  }\n\n  private[this] def updatePos(change: Int) = {\n    if (change != 0) {\n      content.offsetTopAndBottom(change)\n      invalidate()\n    }\n  }\n\n  private[this] def childInTop: Boolean = !content.canScrollVertically(-1)\n\n}\n\ncase class PullingListener(\n    start: () => Unit = () => (),\n    end: () => Unit = () => (),\n    scroll: (Int, Boolean) => Unit = (_, _) => ())\n\ncase class HorizontalMovementListener(\n    start: () => Unit = () => (),\n    end: (Swiping, Int) => Unit = (_, _) => (),\n    scroll: (Int) => Unit = (_) => ())\n\nsealed trait PullType\n\ncase object Pulling extends PullType\n\ncase object HorizontalMovement extends PullType\n\ncase object NoMovement extends PullType\n\ncase class PullToDownStatuses(\n    distanceToValidAction: Int,\n    resistance: Float = 3f,\n    lastPosX: Int = 0,\n    currentPosX: Int = 0,\n    lastPosY: Int = 0,\n    currentPosY: Int = 0,\n    startX: Float = 0,\n    startY: Float = 0,\n    lastMoveX: Float = 0,\n    lastMoveY: Float = 0,\n    offsetX: Float = 0,\n    offsetY: Float = 0,\n    enabled: Boolean = true,\n    scrollHorizontalEnabled: Boolean = false,\n    action: PullType = NoMovement) {\n\n  val posStart = 0\n\n  def restart(): PullToDownStatuses =\n    copy(\n      action = NoMovement,\n      lastPosX = 0,\n      currentPosX = 0,\n      lastPosY = 0,\n      currentPosY = 0,\n      startX = 0,\n      startY = 0,\n      lastMoveX = 0,\n      lastMoveY = 0,\n      offsetX = 0,\n      offsetY = 0)\n\n  def dontStarted: Boolean = startX == 0 && startY == 0\n\n  def start(x: Float, y: Float, action: PullType): PullToDownStatuses =\n    copy(\n      currentPosX = 0,\n      currentPosY = 0,\n      startX = x,\n      startY = y,\n      lastMoveX = x,\n      lastMoveY = y,\n      action = action)\n\n  def move(x: Float, y: Float): PullToDownStatuses =\n    copy(\n      offsetX = x - lastMoveX,\n      offsetY = (y - lastMoveY) / resistance,\n      lastMoveX = x,\n      lastMoveY = y)\n\n  def updateCurrentPostX(current: Int): PullToDownStatuses =\n    copy(lastPosX = currentPosX, currentPosX = current)\n\n  def updateCurrentPostY(current: Int): PullToDownStatuses =\n    copy(lastPosY = currentPosY, currentPosY = current)\n\n  def hasLeftStartPosition: Boolean = currentPosY > posStart\n\n  def isInStartPosition: Boolean = currentPosY == posStart\n\n  def willOverTop(to: Int): Boolean = to < posStart\n\n  def isValidAction: Boolean = currentPosY > distanceToValidAction\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/PullToTabsView.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.support.v4.view.MotionEventCompat\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, MotionEvent, ViewGroup}\nimport android.widget.LinearLayout\nimport android.widget.LinearLayout.LayoutParams\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.layouts.tweaks.PullToDownViewTweaks._\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.commons._\nimport cards.nine.models._\nimport cards.nine.models.types.theme.{DrawerTabsBackgroundColor, PrimaryColor, SearchIconsColor}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nclass PullToTabsView(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends PullToDownView(context, attr, defStyleAttr) {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  val heightTabs = resGetDimensionPixelSize(R.dimen.pulltotabs_max_height)\n\n  val distanceChangeTabs = resGetDimensionPixelSize(R.dimen.pulltotabs_distance_change_tabs)\n\n  var tabs = slot[LinearLayout]\n\n  var pullToTabsStatuses = PullToTabsStatuses()\n\n  var tabsListener = PullToTabsListener()\n\n  override def dispatchTouchEvent(event: MotionEvent): Boolean = {\n    if (pullToDownStatuses.action == Pulling) {\n      val displacedX = MotionEventCompat.getX(event, 0) + (distanceChangeTabs / 2)\n      val pos        = math.floor((displacedX - pullToDownStatuses.startX) / distanceChangeTabs).toInt\n      val newPos     = pullToTabsStatuses.calculatePosition(pos, getTabsCount)\n      if (newPos != pullToTabsStatuses.selectedItem) {\n        pullToTabsStatuses = pullToTabsStatuses.copy(selectedItem = newPos)\n        (tabs <~ activateItem(newPos)).run\n      }\n    }\n    super.dispatchTouchEvent(event)\n  }\n\n  override def drop(): Unit = {\n    if (pullToTabsStatuses.wasTabChanged()) {\n      tabsListener.changeItem(pullToTabsStatuses.selectedItem)\n    }\n    super.drop()\n  }\n\n  def getTabsCount = tabs map (_.getChildCount) getOrElse 0\n\n  def linkTabsView(\n      tabsView: Option[LinearLayout],\n      start: () => Ui[Any],\n      end: () => Ui[Any]): Ui[PullToTabsView] = {\n    tabs = tabsView\n    this <~\n      pdvPullingListener(\n        PullingListener(\n          start = () => {\n            pullToTabsStatuses = pullToTabsStatuses.start()\n            ((tabs <~ vVisible <~ vY(-heightTabs)) ~ start()).run\n          },\n          end = () => ((tabs <~ vGone) ~ end()).run,\n          scroll = (scroll: Int, close: Boolean) => (tabs <~ vY(-heightTabs + scroll)).run))\n  }\n\n  def selectItem(item: Int): Ui[Any] =\n    (tabs <~ activateItem(item)) ~ Ui(pullToTabsStatuses = pullToTabsStatuses.restart())\n\n  private[this] def activateItem(item: Int): Transformer = Transformer {\n    case tab: TabView if tab.isPosition(item) => tab.activate()\n    case tab: TabView                         => tab.deactivate()\n  }\n\n  def clear(): Unit = (tabs <~ vgRemoveAllViews).run\n\n  def addTabs(items: Seq[TabInfo], colorPrimary: Option[Int] = None, index: Option[Int] = None)(\n      implicit theme: NineCardsTheme): Unit = {\n    index foreach (i => pullToTabsStatuses = pullToTabsStatuses.copy(selectedItem = i))\n    val views = items.zipWithIndex map {\n      case (item, pos) =>\n        new TabView(item, pos, index contains pos, colorPrimary)\n    }\n    val params = new LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1)\n    (tabs <~ vgAddViews(views, params)).run\n  }\n\n  class TabView(item: TabInfo, pos: Int, selected: Boolean, color: Option[Int])(\n      implicit theme: NineCardsTheme)\n      extends LinearLayout(context)\n      with TypedFindView {\n\n    LayoutInflater.from(context).inflate(R.layout.tab_item, this)\n\n    lazy val line = findView(TR.tab_item_line)\n\n    lazy val icon = findView(TR.tab_item_icon)\n\n    lazy val name = findView(TR.tab_item_name)\n\n    val primaryColor = color getOrElse theme.get(PrimaryColor)\n\n    val defaultColor = theme.get(SearchIconsColor)\n\n    val backgroundColor = theme.get(DrawerTabsBackgroundColor)\n\n    ((this <~ vBackgroundColor(backgroundColor)) ~\n      (icon <~ ivSrc(item.drawable)) ~\n      (name <~ tvText(item.name)) ~\n      (if (selected) {\n         activate()\n       } else {\n         deactivate()\n       }) ~\n      (this <~\n        vSetPosition(pos) <~\n        On.click {\n          Ui {\n            pullToTabsStatuses = pullToTabsStatuses.copy(selectedItem = pos)\n            tabsListener.changeItem(pullToTabsStatuses.selectedItem)\n          } ~ (tabs <~ activateItem(pos))\n        })).run\n\n    def activate(): Ui[_] =\n      (icon <~ tivDefaultColor(primaryColor)) ~\n        (name <~ tvColor(primaryColor)) ~\n        (line <~ vBackgroundColor(primaryColor))\n\n    def deactivate(): Ui[_] =\n      (icon <~ tivDefaultColor(defaultColor)) ~\n        (name <~ tvColor(defaultColor)) ~\n        (line <~ vBlankBackground)\n\n  }\n\n}\n\ncase class PullToTabsListener(changeItem: (Int) => Unit = (_) => ())\n\ncase class PullToTabsStatuses(selectedItem: Int = 0, selectedItemWhenStartPulling: Int = 0) {\n\n  def start() = copy(selectedItemWhenStartPulling = selectedItem)\n\n  def wasTabChanged() = selectedItemWhenStartPulling != selectedItem\n\n  def calculatePosition(pos: Int, max: Int) = {\n    val min = math.max(pos + selectedItemWhenStartPulling, 0)\n    math.min(min, max - 1)\n  }\n\n  def restart(): PullToTabsStatuses =\n    copy(selectedItem = 0, selectedItemWhenStartPulling = 0)\n}\n\ntrait PullToTabsViewStyles {\n\n  def tabContentStyles(paddingRight: Int = 0)(\n      implicit context: ContextWrapper): Tweak[LinearLayout] = {\n    val heightTabs = resGetDimensionPixelSize(R.dimen.pulltotabs_max_height)\n    vContentSizeMatchWidth(heightTabs) +\n      vPadding(paddingRight = paddingRight) +\n      vGone\n  }\n\n}\n\ncase class TabInfo(drawable: Int, name: String)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/SearchBoxView.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view._\nimport android.widget.{EditText, FrameLayout, LinearLayout}\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.app.ui.components.widgets.{AppsView, ContactView, ContentView}\nimport cards.nine.app.ui.components.drawables.tweaks.PathMorphDrawableTweaks._\nimport cards.nine.app.ui.launcher.LauncherActivity\nimport cards.nine.commons._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.models._\nimport cards.nine.models.types.theme.{\n  SearchBackgroundColor,\n  SearchIconsColor,\n  SearchPressedColor,\n  SearchTextColor\n}\nimport macroid.extras.EditTextTweaks._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nclass SearchBoxView(context: Context, attrs: AttributeSet, defStyle: Int)\n    extends FrameLayout(context, attrs, defStyle)\n    with TypedFindView\n    with Contexts[View] { self =>\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attrs: AttributeSet) = this(context, attrs, 0)\n\n  var listener: Option[SearchBoxAnimatedListener] = None\n\n  val content =\n    LayoutInflater.from(getContext).inflate(TR.layout.search_box_panel, self)\n\n  lazy val editText = findView(TR.launcher_search_box_text)\n\n  lazy val icon = findView(TR.launcher_search_box_icon)\n\n  lazy val action = findView(TR.launcher_search_box_action)\n\n  lazy val headerIcon = findView(TR.launcher_header_icon)\n\n  val appDrawerJobs = context match {\n    case activity: LauncherActivity => activity.appDrawerJobs\n    case _                          => throw new RuntimeException(\"AppDrawerJobs not found\")\n  }\n\n  val navigationJobs = context match {\n    case activity: LauncherActivity => activity.navigationJobs\n    case _                          => throw new RuntimeException(\"NavigationJobs not found\")\n  }\n\n  val headerIconDrawable = PathMorphDrawable(\n    defaultIcon = IconTypes.BURGER,\n    defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_default),\n    padding = resGetDimensionPixelSize(R.dimen.padding_default))\n\n  (self <~ vgAddView(content)).run\n\n  def updateContentView(contentView: ContentView)(implicit theme: NineCardsTheme): Ui[_] = {\n    headerIconDrawable.setColor(theme.get(SearchIconsColor))\n    (icon <~\n      tivDefaultColor(theme.get(SearchIconsColor)) <~\n      tivPressedColor(theme.get(SearchPressedColor)) <~\n      On.click {\n        Ui(listener foreach (_.onOptionsClick()))\n      }) ~\n      (editText <~\n        searchBoxNameStyle(contentView match {\n          case AppsView    => R.string.searchApps\n          case ContactView => R.string.searchContacts\n        }) <~\n        etClickActionSearch((query) => {\n          appDrawerJobs.loadSearch(query).resolveAsync()\n        })) ~\n      (action <~\n        tivDefaultColor(theme.get(SearchIconsColor)) <~\n        tivPressedColor(theme.get(SearchPressedColor)) <~\n        (contentView match {\n          case AppsView =>\n            ivSrc(R.drawable.app_drawer_icon_google_play) +\n              On.click(Ui(navigationJobs.launchPlayStore().resolveAsync()))\n          case ContactView =>\n            ivSrc(R.drawable.app_drawer_icon_phone) +\n              On.click(Ui(navigationJobs.launchDial().resolveAsync()))\n        })) ~\n      (headerIcon <~\n        ivSrc(headerIconDrawable) <~\n        On.click {\n          Ui(listener foreach (_.onHeaderIconClick()))\n        }) ~\n      (content <~ searchBoxContentStyle)\n  }\n\n  def updateHeaderIcon(icon: Int)(implicit theme: NineCardsTheme): Ui[_] =\n    headerIcon <~ pmdAnimIcon(icon)\n\n  def showKeyboard: Ui[_] = editText <~ etShowKeyboard\n\n  def clean: Ui[_] =\n    editText <~ (if (isEmpty) Tweak.blank else tvText(\"\")) <~ etHideKeyboard\n\n  def enableSearch: Ui[_] = editText <~ Tweak[EditText] { view =>\n    view.setEnabled(true)\n    view.setFocusable(true)\n    view.setFocusableInTouchMode(true)\n  }\n\n  def disableSearch: Ui[_] = editText <~ Tweak[EditText] { view =>\n    view.setEnabled(false)\n    view.setFocusable(false)\n  }\n\n  def addTextChangedListener(onChangeText: (String) => Unit): Unit =\n    (editText <~\n      etAddTextChangedListener(\n        (text: String, start: Int, before: Int, count: Int) => onChangeText(text))).run\n\n  def isEmpty: Boolean = Option(editText.getText) exists (_.toString == \"\")\n\n  private[this] def searchBoxContentStyle(implicit theme: NineCardsTheme): Tweak[LinearLayout] =\n    vBackgroundBoxWorkspace(theme.get(SearchBackgroundColor))\n\n  private[this] def searchBoxNameStyle(resourceId: Int)(\n      implicit theme: NineCardsTheme): Tweak[EditText] =\n    tvHint(resourceId) +\n      tvColor(theme.get(SearchTextColor)) +\n      tvHintColor(theme.get(SearchTextColor).alpha(0.8f))\n\n}\n\ncase class SearchBoxAnimatedListener(\n    onHeaderIconClick: () => Unit = () => {},\n    onOptionsClick: () => Unit = () => {})\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/SlidingTabLayout.scala",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.graphics.{Canvas, Color, Paint}\nimport android.support.v4.view.ViewPager\nimport android.util.AttributeSet\nimport android.view.View.OnClickListener\nimport android.view.ViewGroup.LayoutParams._\nimport android.view.{Gravity, LayoutInflater, View}\nimport android.widget.{FrameLayout, HorizontalScrollView, LinearLayout, TextView}\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.R\n\n/**\n * This file is part of Google Examples, you can see the code here\n * https://developer.android.com/samples/SlidingTabsBasic/index.html\n * We have translated the code to Scala using a functional approach\n */\nclass SlidingTabLayout(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends HorizontalScrollView(context, attr, defStyleAttr) {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  private var viewPager: Option[ViewPager] = None\n\n  private var viewPagerPageChangeListener: Option[ViewPager.OnPageChangeListener] = None\n\n  val tabStrip: SlidingTabStrip = new SlidingTabStrip(context)\n\n  var lastScrollTo: Int = 0\n\n  var defaultTextColor: Int =\n    context.getResources.getColor(R.color.text_tab_color_default)\n\n  var selectedTextColor: Int =\n    context.getResources.getColor(R.color.text_tab_color_selected)\n\n  val params = new FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)\n  params.gravity = Gravity.BOTTOM\n\n  setHorizontalScrollBarEnabled(false)\n  setFillViewport(true)\n\n  addView(tabStrip, params)\n\n  def setTabStripColor(color: Int) = tabStrip.setColor(color)\n\n  def setDefaultTextColor(color: Int) = defaultTextColor = color\n\n  def setSelectedTextColor(color: Int) = selectedTextColor = color\n\n  def setOnPageChangeListener(listener: ViewPager.OnPageChangeListener) =\n    viewPagerPageChangeListener = Some(listener)\n\n  def setViewPager(viewPager: ViewPager) = {\n    tabStrip.removeAllViews()\n    this.viewPager = Option(viewPager)\n\n    viewPager.addOnPageChangeListener(new InternalViewPagerListener())\n    val adapter = viewPager.getAdapter\n    (0 until adapter.getCount) foreach { i =>\n      val tabTitleView = createDefaultTabView(i)\n      tabTitleView.setText(adapter.getPageTitle(i))\n      tabTitleView.setOnClickListener(new TabClickListener(i))\n      tabStrip.addView(tabTitleView)\n    }\n    updateTabsColors(viewPager.getCurrentItem)\n  }\n\n  def createDefaultTabView(position: Int): TextView = {\n    val textView = LayoutInflater\n      .from(context)\n      .inflate(R.layout.collections_detail_tab, javaNull)\n      .asInstanceOf[TextView]\n    textView.setTextColor(defaultTextColor)\n    textView.setTag(position.toString)\n    textView\n  }\n\n  override def onAttachedToWindow() = {\n    super.onAttachedToWindow()\n    viewPager foreach (vp => scrollToTab(vp.getCurrentItem, 0))\n  }\n\n  private def scrollToTab(tabIndex: Int, positionOffset: Int) = {\n    val tabStripChildCount = tabStrip.getChildCount\n    if (tabStripChildCount != 0 && tabIndex > 0 || tabIndex < tabStripChildCount) {\n      val selectedChild = tabStrip.getChildAt(tabIndex)\n      if (selectedChild != javaNull && selectedChild.getMeasuredWidth != 0) {\n        val targetScrollX = ((positionOffset + selectedChild.getLeft) - getWidth / 2) + selectedChild.getWidth / 2\n        if (targetScrollX != lastScrollTo) {\n          scrollTo(targetScrollX, 0)\n          lastScrollTo = targetScrollX\n        }\n      }\n    }\n  }\n\n  private def updateTabsColors(position: Int) = {\n    (0 until tabStrip.getChildCount) foreach {\n      tabStrip.getChildAt(_) match {\n        case text: TextView\n            if Option(text.getTag).isDefined && text.getTag.equals(position.toString) =>\n          text.setTextColor(selectedTextColor)\n        case text: TextView => text.setTextColor(defaultTextColor)\n        case _              =>\n      }\n    }\n  }\n\n  private class InternalViewPagerListener extends ViewPager.OnPageChangeListener {\n    private var scrollState: Int = 0\n\n    def onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {\n      val tabStripChildCount: Int = tabStrip.getChildCount\n      if (tabStripChildCount != 0 && position > 0 || position < tabStripChildCount) {\n        tabStrip.onViewPagerPageChanged(position, positionOffset)\n        val selectedTitle: View = tabStrip.getChildAt(position)\n        val selectedOffset: Int =\n          if (selectedTitle == javaNull) 0 else selectedTitle.getWidth\n        val nextTitlePosition: Int = position + 1\n        val nextTitle: View        = tabStrip.getChildAt(nextTitlePosition)\n        val nextOffset: Int =\n          if (nextTitle == javaNull) 0 else nextTitle.getWidth\n        val extraOffset: Int =\n          (0.5F * (positionOffset * (selectedOffset + nextOffset).toFloat)).toInt\n        scrollToTab(position, extraOffset)\n        viewPagerPageChangeListener foreach (_.onPageScrolled(\n          position,\n          positionOffset,\n          positionOffsetPixels))\n      }\n    }\n\n    def onPageScrollStateChanged(state: Int) = {\n      scrollState = state\n      viewPagerPageChangeListener foreach (_.onPageScrollStateChanged(state))\n    }\n\n    def onPageSelected(position: Int) = {\n      updateTabsColors(position)\n      if (scrollState == ViewPager.SCROLL_STATE_IDLE) {\n        tabStrip.onViewPagerPageChanged(position, 0f)\n        scrollToTab(position, 0)\n      }\n      viewPagerPageChangeListener foreach (_.onPageSelected(position))\n    }\n  }\n\n  private class TabClickListener(position: Int) extends OnClickListener {\n    def onClick(v: View) = viewPager foreach (_.setCurrentItem(position))\n  }\n\n}\n\nclass SlidingTabStrip(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends LinearLayout(context, attr, defStyleAttr) {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  private var selectedPosition: Int  = 0\n  private var selectionOffset: Float = .0f\n\n  setWillNotDraw(false)\n\n  val selectedIndicatorThickness =\n    context.getResources.getDimensionPixelOffset(R.dimen.height_selected_tab)\n  val selectedIndicatorPaint = new Paint\n  setColor(Color.WHITE)\n\n  def setColor(color: Int) = selectedIndicatorPaint.setColor(color)\n\n  def onViewPagerPageChanged(position: Int, positionOffset: Float) {\n    selectedPosition = position\n    selectionOffset = positionOffset\n    invalidate()\n  }\n\n  protected override def onDraw(canvas: Canvas) {\n    val height: Int     = getHeight\n    val childCount: Int = getChildCount\n\n    if (childCount > 0) {\n      val selectedTitle: View = getChildAt(selectedPosition)\n\n      val initialLeft: Int  = selectedTitle.getLeft\n      val initialRight: Int = selectedTitle.getRight\n\n      val (left, right) =\n        if (selectionOffset > 0f && selectedPosition < (getChildCount - 1)) {\n          val nextTitle = getChildAt(selectedPosition + 1)\n          val l =\n            (selectionOffset * nextTitle.getLeft + (1.0f - selectionOffset) * initialLeft).toInt\n          val r =\n            (selectionOffset * nextTitle.getRight + (1.0f - selectionOffset) * initialRight).toInt\n          (l, r)\n        } else {\n          (initialLeft, initialRight)\n        }\n      canvas\n        .drawRect(left, height - selectedIndicatorThickness, right, height, selectedIndicatorPaint)\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/StepsWorkspaces.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.{FrameLayout, LinearLayout}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.TextViewTweaks._\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid._\n\nclass StepsWorkspaces(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends AnimatedWorkSpaces[StepWorkSpaceWidgetsHolder, StepData](context, attr, defStyleAttr)\n    with Contexts[View] {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  override def createEmptyView(): StepWorkSpaceWidgetsHolder =\n    new StepWorkSpaceWidgetsHolder\n\n  override def createView(viewType: Int): StepWorkSpaceWidgetsHolder =\n    new StepWorkSpaceWidgetsHolder\n\n  override def populateView(\n      view: Option[StepWorkSpaceWidgetsHolder],\n      data: StepData,\n      viewType: Int,\n      position: Int): Ui[_] =\n    view match {\n      case Some(v: StepWorkSpaceWidgetsHolder) => v.bind(data)\n      case _                                   => Ui.nop\n    }\n\n}\n\ncase class StepData(image: Int, color: Int, title: String, message: String)\n\nclass StepWorkSpaceWidgetsHolder(implicit contextWrapper: ContextWrapper)\n    extends LinearLayout(contextWrapper.application)\n    with TypedFindView {\n\n  lazy val image = findView(TR.wizard_step_item_image)\n\n  lazy val title = findView(TR.wizard_step_item_title)\n\n  lazy val message = findView(TR.wizard_step_item_message)\n\n  LayoutInflater.from(contextWrapper.application).inflate(R.layout.wizard_step, this)\n\n  def bind(data: StepData): Ui[_] =\n    (image <~ ivSrc(data.image)) ~\n      (title <~ tvText(data.title) <~ tvColor(data.color)) ~\n      (message <~ tvText(data.message))\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/TopBarLayout.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.FrameLayout\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons.ops.ConditionWeatherOps._\nimport cards.nine.app.ui.commons.ops.NineCardsMomentOps._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.commons.states.MomentState\nimport cards.nine.app.ui.components.drawables.{\n  IconTypes,\n  PathMorphDrawable,\n  TopBarMomentBackgroundDrawable,\n  TopBarMomentEdgeBackgroundDrawable\n}\nimport cards.nine.app.ui.components.models.{\n  CollectionsWorkSpace,\n  LauncherData,\n  MomentWorkSpace,\n  WorkSpaceType\n}\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.app.ui.launcher.jobs.{LauncherJobs, NavigationJobs}\nimport cards.nine.app.ui.preferences.commons._\nimport cards.nine.commons._\nimport cards.nine.models._\nimport cards.nine.models.types.theme.{\n  SearchBackgroundColor,\n  SearchGoogleColor,\n  SearchIconsColor,\n  SearchPressedColor\n}\nimport cards.nine.models.types.{ConditionWeather, NineCardsMoment, UnknownCondition}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nclass TopBarLayout(context: Context, attrs: AttributeSet, defStyle: Int)\n    extends FrameLayout(context, attrs, defStyle)\n    with Contexts[View]\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attrs: AttributeSet) = this(context, attrs, 0)\n\n  val hasWeatherKey = \"has-weather-key\"\n\n  val typeWorkspaceKey = \"type-workspace-key\"\n\n  lazy val momentState = new MomentState\n\n  lazy val collectionsSearchPanel = findView(TR.launcher_search_panel)\n\n  lazy val collectionsBurgerIcon = findView(TR.launcher_burger_icon)\n\n  lazy val collectionsGoogleIcon = findView(TR.launcher_google_icon)\n\n  lazy val collectionsMicIcon = findView(TR.launcher_mic_icon)\n\n  lazy val momentContent = findView(TR.launcher_moment_content)\n\n  lazy val momentIconContent = findView(TR.launcher_moment_icon_content)\n\n  lazy val momentIcon = findView(TR.launcher_moment_icon)\n\n  lazy val momentText = findView(TR.launcher_moment_text)\n\n  lazy val momentUnpin = findView(TR.launcher_moment_unpin)\n\n  lazy val momentWeather = findView(TR.launcher_moment_weather)\n\n  lazy val momentGoogleIcon = findView(TR.launcher_moment_google_icon)\n\n  lazy val momentMicIcon = findView(TR.launcher_moment_mic_icon)\n\n  val headerIconDrawable = PathMorphDrawable(\n    defaultIcon = IconTypes.BURGER,\n    defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_default),\n    padding = resGetDimensionPixelSize(R.dimen.padding_default))\n\n  val collectionWorkspace =\n    LayoutInflater.from(context).inflate(R.layout.collection_bar_view_panel, javaNull)\n\n  val momentWorkspace =\n    LayoutInflater.from(context).inflate(R.layout.moment_bar_view_panel, javaNull)\n\n  (this <~\n    vAddField(typeWorkspaceKey, CollectionsWorkSpace) <~\n    vgAddViews(Seq(momentWorkspace, collectionWorkspace)) <~\n    vInvisible).run\n\n  def init(workSpaceType: WorkSpaceType)(\n      implicit navigationJobs: NavigationJobs,\n      theme: NineCardsTheme): Ui[Any] = {\n    (this <~ vVisible) ~\n      populate ~\n      (workSpaceType match {\n        case CollectionsWorkSpace =>\n          (momentWorkspace <~ vInvisible) ~ (collectionWorkspace <~ vVisible)\n        case MomentWorkSpace => (momentWorkspace <~ vVisible) ~ (collectionWorkspace <~ vInvisible)\n      })\n  }\n\n  def populate(implicit navigationJobs: NavigationJobs, theme: NineCardsTheme): Ui[Any] = {\n\n    val iconColor      = theme.get(SearchIconsColor)\n    val pressedColor   = theme.get(SearchPressedColor)\n    val iconBackground = new TopBarMomentBackgroundDrawable\n    val edgeBackground = new TopBarMomentEdgeBackgroundDrawable\n    val googleLogoPref = GoogleLogo.readValue\n    val googleLogoTweaks = googleLogoPref match {\n      case GoogleLogoTheme =>\n        ivSrc(R.drawable.search_bar_logo_google_light) +\n          tivDefaultColor(theme.get(SearchGoogleColor)) +\n          tivPressedColor(pressedColor)\n      case GoogleLogoColoured =>\n        ivSrc(R.drawable.search_bar_logo_google_color) + tivClean\n    }\n    val micLogoTweaks = googleLogoPref match {\n      case GoogleLogoTheme =>\n        ivSrc(R.drawable.search_bar_mic_light) +\n          tivDefaultColor(theme.get(SearchGoogleColor)) +\n          tivPressedColor(pressedColor)\n      case GoogleLogoColoured =>\n        ivSrc(R.drawable.search_bar_mic_color) + tivClean\n    }\n\n    headerIconDrawable.setColor(iconColor)\n\n    val sizeRes = FontSize.getTitleSizeResource\n    (momentWorkspace <~ vBackground(edgeBackground)) ~\n      (momentIconContent <~ vBackground(iconBackground)) ~\n      (momentIcon <~ tivDefaultColor(iconColor) <~ tivPressedColor(iconColor)) ~\n      (momentText <~ tvSizeResource(sizeRes)) ~\n      (collectionsSearchPanel <~\n        vBackgroundBoxWorkspace(theme.get(SearchBackgroundColor))) ~\n      (collectionsBurgerIcon <~\n        ivSrc(headerIconDrawable) <~\n        On.click(Ui(navigationJobs\n          .openMenu()\n          .resolveAsyncServiceOr(_ => navigationJobs.navigationUiActions.showContactUsError())))) ~\n      (collectionsGoogleIcon <~\n        googleLogoTweaks <~\n        On.click(Ui(navigationJobs\n          .launchSearch()\n          .resolveAsyncServiceOr(_ => navigationJobs.navigationUiActions.showContactUsError())))) ~\n      (collectionsMicIcon <~\n        micLogoTweaks <~\n        On.click(\n          Ui(navigationJobs\n            .launchVoiceSearch()\n            .resolveAsyncServiceOr(_ => navigationJobs.navigationUiActions.showContactUsError()))))\n  }\n\n  def movement(from: LauncherData, to: LauncherData, isFromLeft: Boolean, fraction: Float): Unit =\n    if (from.workSpaceType != to.workSpaceType) {\n      val displacement = getWidth * fraction\n      val fromX        = if (isFromLeft) displacement else -displacement\n      val toX          = fromX + (if (isFromLeft) -getWidth else getWidth)\n      ((if (fraction >= 1) {\n          (this <~ vAddField(typeWorkspaceKey, to.workSpaceType)) ~\n            (getView(from.workSpaceType) <~ vInvisible <~ vTranslationX(0))\n        } else {\n          getView(from.workSpaceType) <~ vVisible <~ vTranslationX(fromX)\n        }) ~\n        (getView(to.workSpaceType) <~\n          vTranslationX(toX) <~\n          vVisible)).run\n    }\n\n  def reloadMoment(moment: NineCardsMoment)(\n      implicit navigationJobs: NavigationJobs,\n      launcherJobs: LauncherJobs,\n      theme: NineCardsTheme): Ui[Any] = {\n    val showMicSearch = ShowMicSearchMoment.readValue\n\n    def unpinTweak =\n      if (momentState.getPersistMoment.contains(moment)) {\n        vVisible +\n          On.click(Ui {\n            (momentUnpin <~ vGone).run\n            launcherJobs.cleanPersistedMoment().resolveAsync()\n          })\n      } else {\n        vGone\n      }\n\n    def weatherTweak =\n      if (ShowWeatherMoment.readValue) {\n        vVisible +\n          On.click(\n            Ui(\n              navigationJobs\n                .launchGoogleWeather()\n                .resolveAsyncServiceOr(_ =>\n                  navigationJobs.navigationUiActions.showContactUsError())))\n      } else {\n        vGone\n      }\n\n    (momentContent <~\n      On.click(Ui(navigationJobs.goToChangeMoment().resolveAsync())) <~\n      On.longClick(Ui(navigationJobs.launchEditMoment(moment.name).resolveAsync()) ~ Ui(true))) ~\n      (momentIcon <~\n        ivSrc(moment.getIconCollectionDetail)) ~\n      (momentText <~\n        tvText(moment.getName)) ~\n      (momentUnpin <~ unpinTweak) ~\n      (momentWeather <~ weatherTweak) ~\n      (momentGoogleIcon <~\n        On.click(\n          Ui(\n            navigationJobs\n              .launchSearch()\n              .resolveServiceOr(_ => navigationJobs.navigationUiActions.showContactUsError())))) ~\n      (momentMicIcon <~\n        (if (showMicSearch) vVisible else vGone) <~\n        On.click(\n          Ui(\n            navigationJobs\n              .launchVoiceSearch()\n              .resolveServiceOr(_ => navigationJobs.navigationUiActions.showContactUsError()))))\n  }\n\n  def reloadByType(workSpaceType: WorkSpaceType): Ui[Any] = workSpaceType match {\n    case MomentWorkSpace\n        if !this.getField[WorkSpaceType](typeWorkspaceKey).contains(MomentWorkSpace) =>\n      (this <~ vAddField(typeWorkspaceKey, MomentWorkSpace)) ~\n        (collectionWorkspace <~ applyFadeOut()) ~\n        (momentWorkspace <~ vTranslationX(0) <~ applyFadeIn())\n    case CollectionsWorkSpace\n        if !this.getField[WorkSpaceType](typeWorkspaceKey).contains(CollectionsWorkSpace) =>\n      (this <~ vAddField(typeWorkspaceKey, CollectionsWorkSpace)) ~\n        (collectionWorkspace <~ vTranslationX(0) <~ applyFadeIn()) ~\n        (momentWorkspace <~ applyFadeOut())\n    case _ => Ui.nop\n  }\n\n  def getView(workSpaceType: WorkSpaceType): Option[View] = workSpaceType match {\n    case MomentWorkSpace      => Some(momentWorkspace)\n    case CollectionsWorkSpace => Some(collectionWorkspace)\n    case _                    => None\n  }\n\n  def setWeather(condition: ConditionWeather): Ui[Any] =\n    (momentWeather.getField[Boolean](hasWeatherKey), condition) match {\n      case (Some(true), UnknownCondition) => Ui.nop\n      case _                              => momentWeather <~ ivSrc(condition.getIcon) <~ vAddField(hasWeatherKey, true)\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/WizardInlineWorkspaces.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.LinearLayout\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.TextViewTweaks._\n\nclass WizardInlineWorkspaces(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends AnimatedWorkSpaces[WizardInlineWidgetsHolder, WizardInlineData](\n      context,\n      attr,\n      defStyleAttr)\n    with Contexts[View] {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  override def createEmptyView(): WizardInlineWidgetsHolder =\n    new WizardInlineWidgetsHolder\n\n  override def createView(viewType: Int): WizardInlineWidgetsHolder =\n    new WizardInlineWidgetsHolder\n\n  override def populateView(\n      view: Option[WizardInlineWidgetsHolder],\n      data: WizardInlineData,\n      viewType: Int,\n      position: Int): Ui[_] =\n    view match {\n      case Some(v: WizardInlineWidgetsHolder) => v.bind(data)\n      case _                                  => Ui.nop\n    }\n\n}\n\ncase class WizardInlineData(image: Int, title: String, message: String)\n\nclass WizardInlineWidgetsHolder(implicit contextWrapper: ContextWrapper)\n    extends LinearLayout(contextWrapper.application)\n    with TypedFindView {\n\n  lazy val image = findView(TR.wizard_inline_item_image)\n\n  lazy val title = findView(TR.wizard_inline_item_title)\n\n  lazy val message = findView(TR.wizard_inline_item_message)\n\n  LayoutInflater.from(contextWrapper.application).inflate(R.layout.wizard_inline_step, this)\n\n  def bind(data: WizardInlineData): Ui[_] =\n    (image <~ ivSrc(data.image)) ~\n      (title <~ tvText(data.title)) ~\n      (message <~ tvText(data.message))\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/WorkSpaceButton.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.ImageView.ScaleType\nimport android.widget.LinearLayout\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.ops.CollectionOps._\nimport cards.nine.app.ui.commons.{GenericUiContext, UiContext}\nimport cards.nine.commons._\nimport cards.nine.models.types.AppCardType\nimport cards.nine.models.types.theme.DrawerTextColor\nimport cards.nine.models.{Card, Collection, NineCardsTheme}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid._\n\nclass WorkSpaceButton(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends LinearLayout(context, attr, defStyleAttr)\n    with Contexts[View]\n    with TypedFindView {\n\n  implicit val uiContext: UiContext[Context] = GenericUiContext(context)\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  LayoutInflater.from(context).inflate(R.layout.workspace_button, this)\n\n  val padding = resGetDimensionPixelSize(R.dimen.padding_small)\n\n  private[this] lazy val content = findView(TR.workspace_moment_icon_content)\n\n  private[this] lazy val title = findView(TR.workspace_moment_title)\n\n  private[this] lazy val icon = findView(TR.workspace_moment_icon)\n\n  def init(t: WorkSpaceButtonType)(implicit theme: NineCardsTheme): Ui[Any] =\n    t match {\n      case WorkSpaceAppMomentButton =>\n        title <~ tvColor(theme.get(DrawerTextColor))\n      case WorkSpaceActionWidgetButton =>\n        (this <~ vBlankBackground) ~\n          (title <~ tvColorResource(R.color.widgets_text))\n    }\n\n  def populateCollection(collection: Collection)(implicit theme: NineCardsTheme): Ui[Any] = {\n    val resIcon = collection.getIconDetail\n    (title <~ tvText(collection.name)) ~\n      (content <~ vPaddings(padding)) ~\n      (icon <~\n        ivScaleType(ScaleType.CENTER_INSIDE) <~\n        vBackgroundCollection(collection.themedColorIndex) <~\n        ivSrc(resIcon))\n  }\n\n  def populateCard(card: Card): Ui[Any] =\n    (title <~ tvText(card.term)) ~\n      (icon <~\n        (card.cardType match {\n          case cardType if cardType.isContact =>\n            ivUriContactFromLookup(card.intent.extractLookup(), card.term, circular = true)\n          case AppCardType => ivSrcByPackageName(card.packageName, card.term)\n          case _           => ivCardUri(card.imagePath, card.term, circular = true)\n        }))\n\n  def populateIcon(resIcon: Int, resTitle: Int, resColor: Int): Ui[Any] = {\n    (title <~ tvText(resTitle)) ~\n      (content <~ vPaddings(padding)) ~\n      (icon <~\n        ivScaleType(ScaleType.CENTER_INSIDE) <~\n        vBackgroundCircle(resGetColor(resColor)) <~\n        ivSrc(resIcon))\n  }\n\n}\n\nsealed trait WorkSpaceButtonType\n\ncase object WorkSpaceAppMomentButton extends WorkSpaceButtonType\n\ncase object WorkSpaceActionWidgetButton extends WorkSpaceButtonType\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/WorkspaceItemMenu.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts\n\nimport android.content.Context\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.OvalShape\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.{FrameLayout, ImageView}\nimport macroid.extras.DeviceVersion.Lollipop\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid._\n\nclass WorkspaceItemMenu(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends FrameLayout(context, attr, defStyleAttr)\n    with Contexts[View]\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  LayoutInflater.from(context).inflate(R.layout.workspace_item_menu, this)\n\n  private[this] val title = Option(findView(TR.workspace_title))\n\n  val icon = Option(findView(TR.workspace_icon))\n\n  (icon <~ fabStyle).run\n\n  def populate(backgroundColor: Int, res: Int, text: Int): Ui[Any] =\n    (title <~ tvText(text)) ~\n      (icon <~\n        ivSrc(res) <~\n        (Lollipop ifSupportedThen {\n          vBackgroundColor(backgroundColor)\n        } getOrElse {\n          val drawable = new ShapeDrawable(new OvalShape)\n          drawable.getPaint.setColor(backgroundColor)\n          vBackground(drawable)\n        }))\n\n  private[this] def fabStyle: Tweak[ImageView] =\n    Lollipop ifSupportedThen {\n      vElevation(resGetDimension(R.dimen.elevation_fab_button)) + vCircleOutlineProvider()\n    } getOrElse Tweak.blank\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/snails/LayoutSnails.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts.snails\n\nimport android.animation.{Animator, AnimatorListenerAdapter}\nimport android.support.v7.widget.RecyclerView\nimport android.view.View\nimport android.view.animation.DecelerateInterpolator\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewTweaks._\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.commons.javaNull\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\n\nimport scala.concurrent.Promise\n\nobject TabsSnails {\n\n  def showTabs(implicit context: ContextWrapper): Snail[View] = Snail[View] { view =>\n    val maxHeight = resGetDimensionPixelSize(R.dimen.pulltotabs_max_height)\n    val height    = resGetDimensionPixelSize(R.dimen.pulltotabs_height)\n    view.clearAnimation()\n    view.setLayerType(View.LAYER_TYPE_HARDWARE, javaNull)\n    val animPromise = Promise[Unit]()\n\n    view.setY(-maxHeight)\n    view.setVisibility(View.VISIBLE)\n\n    view\n      .animate()\n      .setInterpolator(new DecelerateInterpolator)\n      .y(height - maxHeight)\n      .setListener(new AnimatorListenerAdapter {\n        override def onAnimationEnd(animation: Animator) {\n          super.onAnimationEnd(animation)\n          view.setLayerType(View.LAYER_TYPE_NONE, javaNull)\n          animPromise.trySuccess(())\n        }\n      })\n      .start()\n\n    animPromise.future\n  }\n\n  def hideTabs(implicit context: ContextWrapper): Snail[View] = Snail[View] { view =>\n    val maxHeight = resGetDimensionPixelSize(R.dimen.pulltotabs_max_height)\n    view.clearAnimation()\n    view.setLayerType(View.LAYER_TYPE_HARDWARE, javaNull)\n    val animPromise = Promise[Unit]()\n\n    view\n      .animate()\n      .setInterpolator(new DecelerateInterpolator)\n      .y(-maxHeight)\n      .setListener(new AnimatorListenerAdapter {\n        override def onAnimationEnd(animation: Animator) {\n          super.onAnimationEnd(animation)\n          view.setLayerType(View.LAYER_TYPE_NONE, javaNull)\n          view.setVisibility(View.GONE)\n          animPromise.trySuccess(())\n        }\n      })\n      .start()\n\n    animPromise.future\n  }\n\n  def hideList(implicit context: ContextWrapper): Snail[RecyclerView] =\n    Snail[RecyclerView] { view =>\n      val height = resGetDimensionPixelSize(R.dimen.pulltotabs_height)\n      view.clearAnimation()\n      view.setLayerType(View.LAYER_TYPE_HARDWARE, javaNull)\n      val animPromise = Promise[Unit]()\n\n      view\n        .animate()\n        .setInterpolator(new DecelerateInterpolator)\n        .y(height)\n        .setListener(new AnimatorListenerAdapter {\n          override def onAnimationEnd(animation: Animator) {\n            super.onAnimationEnd(animation)\n            view.setLayerType(View.LAYER_TYPE_NONE, javaNull)\n            animPromise.trySuccess(())\n          }\n        })\n        .start()\n\n      animPromise.future\n    }\n\n  def showList(implicit context: ContextWrapper): Snail[RecyclerView] =\n    Snail[RecyclerView] { view =>\n      view.clearAnimation()\n      view.setLayerType(View.LAYER_TYPE_HARDWARE, javaNull)\n      val animPromise = Promise[Unit]()\n\n      view\n        .animate()\n        .setInterpolator(new DecelerateInterpolator)\n        .y(0)\n        .setListener(new AnimatorListenerAdapter {\n          override def onAnimationEnd(animation: Animator) {\n            super.onAnimationEnd(animation)\n            view.setLayerType(View.LAYER_TYPE_NONE, javaNull)\n            animPromise.trySuccess(())\n          }\n        })\n        .start()\n\n      animPromise.future\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/layouts/tweaks/LayoutsTweaks.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.layouts.tweaks\n\nimport android.appwidget.AppWidgetHostView\nimport android.support.v4.view.ViewPager\nimport android.support.v7.widget.RecyclerView\nimport android.support.v7.widget.Toolbar.OnMenuItemClickListener\nimport android.view.{MenuItem, View}\nimport android.widget.LinearLayout\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.commons.ops.WidgetsOps.Cell\nimport cards.nine.app.ui.components.layouts.AnimatedWorkSpaces._\nimport cards.nine.app.ui.components.layouts._\nimport cards.nine.app.ui.components.models.{\n  CollectionsWorkSpace,\n  LauncherData,\n  LauncherMoment,\n  WorkSpaceType\n}\nimport cards.nine.app.ui.components.widgets.ContentView\nimport cards.nine.app.ui.launcher.holders.{\n  LauncherWorkSpaceCollectionsHolder,\n  LauncherWorkSpaceMomentsHolder\n}\nimport cards.nine.app.ui.launcher.jobs.{LauncherJobs, NavigationJobs, WidgetsJobs}\nimport cards.nine.models.types.{\n  ConditionWeather,\n  DialogToolbarTitle,\n  DialogToolbarType,\n  NineCardsMoment\n}\nimport cards.nine.models.{NineCardsTheme, TermCounter, _}\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\nimport macroid.extras.ResourcesExtras._\n\nimport scala.concurrent.ExecutionContext.Implicits.global\n\nobject LauncherWorkSpacesTweaks {\n  type W = LauncherWorkSpaces\n\n  def lwsData(data: Seq[LauncherData], pageSelected: Int) = Tweak[W] { view =>\n    view.init(data, pageSelected)\n  }\n\n  def lwsDataCollections(data: Seq[LauncherData], pageCollectionSelected: Option[Int]) = Tweak[W] {\n    view =>\n      view.data.headOption match {\n        case Some(moment) =>\n          val page = pageCollectionSelected map (_ + 1) getOrElse view.currentPage()\n          view.init(moment +: data, page)\n        case _ =>\n      }\n  }\n\n  def lwsDataMoment(moment: LauncherData) = Tweak[W](_.reloadMoment(moment))\n\n  def lwsReloadMomentCollection(collection: Option[Collection]) =\n    Tweak[W](_.changeCollectionInMoment(collection))\n\n  def lwsAddWidget(widgetView: AppWidgetHostView, cell: Cell, widget: Widget) =\n    Tweak[W](_.addWidget(widgetView, cell, widget))\n\n  def lwsAddNoConfiguredWidget(wCell: Int, hCell: Int, widget: Widget) =\n    Tweak[W](_.addNoConfiguredWidget(wCell, hCell, widget))\n\n  def lwsReplaceWidget(widgetView: AppWidgetHostView, wCell: Int, hCell: Int, widget: Widget) =\n    Tweak[W](_.addReplaceWidget(widgetView, wCell, hCell, widget))\n\n  def lwsStartEditWidgets() = Tweak[W](_.startEditWidget())\n\n  def lwsCloseEditWidgets() = Tweak[W](_.closeEditWidget())\n\n  def lwsReloadSelectedWidget() = Tweak[W](_.reloadSelectedWidget())\n\n  def lwsClearWidgets() = Tweak[W](_.clearWidgets())\n\n  def lwsUnhostWidget(id: Int) = Tweak[W](_.unhostWidget(id))\n\n  def lwsClean = Tweak[W](_.clean())\n\n  def lwsOpenMenu = Tweak[W](_.openMenu())\n\n  def lwsListener(listener: LauncherWorkSpacesListener) =\n    Tweak[W](_.workSpacesListener = listener)\n\n  def lwsSelect(position: Int) = Tweak[W](_.selectPosition(position))\n\n  def lwsCloseMenu = Snail[W](_.closeMenu().get map (_ => ()))\n\n  def lwsPrepareItemsScreenInReorder(position: Int) =\n    Tweak[W](_.prepareItemsScreenInReorder(position).run)\n\n  def lwsDragReorderWidgetDispatcher(action: Int, x: Float, y: Float) =\n    Tweak[W] {\n      _.getCurrentView match {\n        case Some(holder: LauncherWorkSpaceMomentsHolder) =>\n          holder.dragReorderController(action, x, y)\n        case _ =>\n      }\n    }\n\n  def lwsDragAddItemDispatcher(action: Int, x: Float, y: Float) = Tweak[W] {\n    _.getCurrentView match {\n      case Some(holder: LauncherWorkSpaceCollectionsHolder) =>\n        holder.dragAddItemController(action, x, y)\n      case _ =>\n    }\n  }\n\n  def lwsDragReorderCollectionDispatcher(action: Int, x: Float, y: Float) =\n    Tweak[W] {\n      _.getCurrentView match {\n        case Some(holder: LauncherWorkSpaceCollectionsHolder) =>\n          holder.dragReorderCollectionController(action, x, y)\n        case _ =>\n      }\n    }\n\n  def lwsAddMovementObserver(observer: ((LauncherData, LauncherData, Boolean, Float) => Unit)) =\n    Tweak[W](_.addMovementObservers(observer))\n\n  def lwsCurrentPage() = Excerpt[W, Int](_.currentPage())\n\n  def lwsCountCollections() = Excerpt[W, Int](_.getCountCollections)\n\n  def lwsGetCollections() = Excerpt[W, Seq[Collection]](_.getCollections)\n\n  def lwsEmptyCollections() = Excerpt[W, Boolean](_.isEmptyCollections)\n\n  def lwsCanMoveToNextScreen() = Excerpt[W, Boolean](_.nextScreen.isDefined)\n\n  def lwsNextScreen() = Excerpt[W, Option[Int]](_.nextScreen)\n\n  def lwsPreviousScreen() = Excerpt[W, Option[Int]](_.previousScreen)\n\n  def lwsCanMoveToPreviousScreen() =\n    Excerpt[W, Boolean](_.previousScreen.isDefined)\n\n  def lwsIsCollectionWorkspace(page: Int) =\n    Excerpt[W, Boolean](_.isCollectionWorkSpace(page))\n\n  def lwsIsCollectionWorkspace = Excerpt[W, Boolean](_.isCollectionWorkSpace)\n\n  def lwsCanMoveToPreviousScreenOnlyCollections() = Excerpt[W, Boolean] { view =>\n    (for {\n      previousScreen <- view.previousScreen\n    } yield view.isCollectionWorkSpace(previousScreen)) getOrElse false\n  }\n\n  def lwsCanMoveToNextScreenOnlyCollections() = Excerpt[W, Boolean] { view =>\n    (for {\n      nextScreen <- view.nextScreen\n    } yield view.isCollectionWorkSpace(nextScreen)) getOrElse false\n  }\n\n}\n\nobject AnimatedWorkSpacesTweaks {\n\n  type W = AnimatedWorkSpaces[_, _]\n\n  def awsListener(listener: AnimatedWorkSpacesListener) =\n    Tweak[W](_.listener = listener)\n\n  def awsDisabled() =\n    Tweak[W](aws =>\n      aws.animatedWorkspaceStatuses = aws.animatedWorkspaceStatuses.copy(enabled = false))\n\n  def awsEnabled() =\n    Tweak[W](aws =>\n      aws.animatedWorkspaceStatuses = aws.animatedWorkspaceStatuses.copy(enabled = true))\n\n  def awsAddPageChangedObserver(observer: PageChangedObserver) =\n    Tweak[W](_.addPageChangedObservers(observer))\n\n  def awsCurrentWorkSpace() =\n    Excerpt[W, Int](_.animatedWorkspaceStatuses.currentItem)\n\n  def awsCountWorkSpace() = Excerpt[W, Int](_.getWorksSpacesCount)\n\n}\n\nobject FabItemMenuTweaks {\n  type W = FabItemMenu\n\n  def fimBackgroundColor(color: Int) = Tweak[W](_.changeBackground(color).run)\n\n  def fimPopulate(backgroundColor: Int, resourceId: Int, text: Int) =\n    Tweak[W](_.populate(backgroundColor, resourceId, text).run)\n\n}\n\nobject WorkSpaceItemMenuTweaks {\n  type W = WorkspaceItemMenu\n\n  def wimPopulate(backgroundColor: Int, resourceId: Int, text: Int) =\n    Tweak[W](_.populate(backgroundColor, resourceId, text).run)\n\n}\n\nobject WorkSpaceButtonTweaks {\n  type W = WorkSpaceButton\n\n  def wbInit(t: WorkSpaceButtonType)(implicit theme: NineCardsTheme) =\n    Tweak[W](_.init(t).run)\n\n  def wbPopulateCollection(collection: Collection)(implicit theme: NineCardsTheme) =\n    Tweak[W](_.populateCollection(collection).run)\n\n  def wbPopulateCard(card: Card) = Tweak[W](_.populateCard(card).run)\n\n  def wbPopulateIcon(resIcon: Int, resTitle: Int, resColor: Int) =\n    Tweak[W](_.populateIcon(resIcon, resTitle, resColor).run)\n\n}\n\nobject StepsWorkspacesTweaks {\n  type W = StepsWorkspaces\n\n  def swData(data: Seq[StepData]) = Tweak[W](_.init(data))\n\n  def swAddMovementObserver(observer: ((StepData, StepData, Boolean, Float) => Unit)) =\n    Tweak[W](_.addMovementObservers(observer))\n\n}\n\nobject WizardInlineWorkspacesTweaks {\n  type W = WizardInlineWorkspaces\n\n  def wiwData(data: Seq[WizardInlineData]) = Tweak[W](_.init(data))\n\n  def wiwAddMovementObserver(\n      observer: ((WizardInlineData, WizardInlineData, Boolean, Float) => Unit)) =\n    Tweak[W](_.addMovementObservers(observer))\n\n}\n\nobject SearchBoxesViewTweaks {\n  type W = SearchBoxView\n\n  def sbvUpdateContentView(contentView: ContentView)(implicit theme: NineCardsTheme) =\n    Tweak[W](_.updateContentView(contentView).run)\n\n  def sbvChangeListener(listener: SearchBoxAnimatedListener) =\n    Tweak[W](_.listener = Some(listener))\n\n  def sbvUpdateHeaderIcon(icon: Int)(implicit theme: NineCardsTheme) =\n    Tweak[W](_.updateHeaderIcon(icon).run)\n\n  def sbvOnChangeText(onChangeText: (String) => Unit) =\n    Tweak[W](_.addTextChangedListener(onChangeText))\n\n  def sbvShowKeyboard = Tweak[W](_.showKeyboard.run)\n\n  def sbvClean = Tweak[W](_.clean.run)\n\n  def sbvEnableSearch = Tweak[W](_.enableSearch.run)\n\n  def sbvDisableSearch = Tweak[W](_.disableSearch.run)\n}\n\nobject TabsViewTweaks {\n\n  val openedField = \"opened\"\n\n  def tvOpen: Tweak[View] = vAddField(openedField, true)\n\n  def tvClose: Tweak[View] = vAddField(openedField, false)\n\n  def isOpened =\n    Excerpt[LinearLayout, Boolean](_.getField[Boolean](openedField) getOrElse false)\n\n}\n\nobject PullToTabsViewTweaks {\n\n  def ptvAddTabsAndActivate(items: Seq[TabInfo], index: Int, colorPrimary: Option[Int])(\n      implicit theme: NineCardsTheme) =\n    Tweak[PullToTabsView](_.addTabs(items, colorPrimary, Some(index)))\n\n  def ptvAddTabs(items: Seq[TabInfo], colorPrimary: Option[Int])(implicit theme: NineCardsTheme) =\n    Tweak[PullToTabsView](_.addTabs(items, colorPrimary))\n\n  def ptvLinkTabs(tabs: Option[LinearLayout], start: () => Ui[Any], end: () => Ui[Any]) =\n    Tweak[PullToTabsView] { view =>\n      view.linkTabsView(tabs, start, end).run\n    }\n\n  def ptvClearTabs() = Tweak[PullToTabsView](_.clear())\n\n  def ptvActivate(item: Int) = Tweak[PullToTabsView](_.selectItem(item).run)\n\n  def ptvListener(pullToTabsListener: PullToTabsListener) =\n    Tweak[PullToTabsView](_.tabsListener = pullToTabsListener)\n\n}\n\nobject PullToCloseViewTweaks {\n\n  def pcvListener(pullToCloseListener: PullToCloseListener) =\n    Tweak[PullToCloseView](_.closeListeners = pullToCloseListener)\n\n}\n\nobject PullToDownViewTweaks {\n\n  def pdvPullingListener(pullToDownListener: PullingListener) =\n    Tweak[PullToDownView](_.pullingListeners = pullToDownListener)\n\n  def pdvHorizontalListener(horizontalMovementListener: HorizontalMovementListener) =\n    Tweak[PullToDownView](_.horizontalListener = horizontalMovementListener)\n\n  def pdvEnable(enabled: Boolean) =\n    Tweak[PullToDownView] { view =>\n      view.pullToDownStatuses = view.pullToDownStatuses.copy(enabled = enabled)\n    }\n\n  def pdvIsEnabled() =\n    Excerpt[PullToDownView, Boolean](_.pullToDownStatuses.enabled)\n\n  def pdvHorizontalEnable(enabled: Boolean) =\n    Tweak[PullToDownView] { view =>\n      view.pullToDownStatuses = view.pullToDownStatuses.copy(scrollHorizontalEnabled = enabled)\n    }\n\n  def pdvResistance(resistance: Float) =\n    Tweak[PullToDownView] { view =>\n      view.pullToDownStatuses = view.pullToDownStatuses.copy(resistance = resistance)\n    }\n\n  def pdvIsPulling() =\n    Excerpt[PullToDownView, Boolean](_.pullToDownStatuses.action == Pulling)\n\n}\n\nobject FastScrollerLayoutTweak {\n  // We should launch this tweak when the adapter has been added\n  def fslLinkRecycler(recyclerView: RecyclerView) =\n    Tweak[FastScrollerLayout](_.linkRecycler(recyclerView))\n\n  def fslColor(color: Int, backgroundColor: Int) =\n    Tweak[FastScrollerLayout](_.setColor(color, backgroundColor))\n\n  def fslMarginRightBarContent(pixels: Int) =\n    Tweak[FastScrollerLayout](_.setMarginRightBarContent(pixels))\n\n  def fslEnabledScroller(enabled: Boolean) =\n    Tweak[FastScrollerLayout](_.setEnabledScroller(enabled))\n\n  def fslReset = Tweak[FastScrollerLayout](_.reset)\n\n  def fslCounters(counters: Seq[TermCounter]) =\n    Tweak[FastScrollerLayout](_.setCounters(counters))\n\n  def fslSignalType(signalType: FastScrollerSignalType) =\n    Tweak[FastScrollerLayout](_.setSignalType(signalType))\n\n}\n\nobject SlidingTabLayoutTweaks {\n  type W = SlidingTabLayout\n\n  def stlViewPager(viewPager: ViewPager): Tweak[W] =\n    Tweak[W](_.setViewPager(viewPager))\n\n  def stlDefaultTextColor(color: Int): Tweak[W] =\n    Tweak[W](_.setDefaultTextColor(color))\n\n  def stlSelectedTextColor(color: Int): Tweak[W] =\n    Tweak[W](_.setSelectedTextColor(color))\n\n  def stlTabStripColor(color: Int): Tweak[W] =\n    Tweak[W](_.setTabStripColor(color))\n\n  def stlOnPageChangeListener(listener: ViewPager.OnPageChangeListener): Tweak[W] =\n    Tweak[W](_.setOnPageChangeListener(listener))\n}\n\nobject DialogToolbarTweaks {\n\n  type W = DialogToolbar\n\n  def dtbInit(color: Int, dialogToolbarType: DialogToolbarType = DialogToolbarTitle)(\n      implicit contextWrapper: ContextWrapper) =\n    Tweak[W](_.init(color, dialogToolbarType).run)\n\n  def dtbExtended(implicit contextWrapper: ContextWrapper) = Tweak[W] {\n    _.changeToolbarHeight(resGetDimensionPixelSize(R.dimen.height_extended_toolbar_dialog)).run\n  }\n\n  def dtbAddExtendedView(viewToAdd: View)(implicit contextWrapper: ContextWrapper) = Tweak[W] {\n    _.addExtendedView(viewToAdd).run\n  }\n\n  def dtbSetIcon(icon: Int) = Tweak[W](_.changeIcon(icon).run)\n\n  def dtbChangeText(resourceId: Int) = Tweak[W](_.changeText(resourceId).run)\n\n  def dtbChangeText(text: String) = Tweak[W](_.changeText(text).run)\n\n  def dtbChangeSearchText(resourceId: Int) =\n    Tweak[W](_.changeSearchText(resourceId).run)\n\n  def dtbChangeSearchText(text: String) =\n    Tweak[W](_.changeSearchText(text).run)\n\n  def dtbResetText() = Tweak[W](_.changeSearchText().run)\n\n  def dtbOnSearchTextChangedListener(onChanged: (String, Int, Int, Int) => Unit) =\n    Tweak[W](_.onSearchTextChangedListener(onChanged).run)\n\n  def dtbClickActionSearch(performSearch: (String) => Unit) =\n    Tweak[W](_.clickActionSearch(performSearch).run)\n\n  def dtbHideKeyboardSearchText() = Tweak[W](_.hideKeyboardSearchText().run)\n\n  def dtbNavigationOnClickListener(click: (View) => Ui[_]) =\n    Tweak[W](_.navigationClickListener(click).run)\n\n  def dtvInflateMenu(res: Int) = Tweak[W](_.toolbar.inflateMenu(res))\n\n  def dtvOnMenuItemClickListener(onItem: (Int) => Boolean) = Tweak[W] { view =>\n    view.toolbar.setOnMenuItemClickListener(new OnMenuItemClickListener {\n      override def onMenuItemClick(menuItem: MenuItem): Boolean =\n        onItem(menuItem.getItemId)\n    })\n  }\n\n}\n\nobject DockAppsPanelLayoutTweaks {\n  type W = DockAppsPanelLayout\n\n  def daplInit(\n      dockApps: Seq[DockAppData])(implicit theme: NineCardsTheme, uiContext: UiContext[_]) =\n    Tweak[W](_.init(dockApps).run)\n\n  def daplDragDispatcher(action: Int, x: Float, y: Float) =\n    Tweak[W](_.dragAddItemController(action, x, y))\n\n  def daplReload(dockApp: DockAppData)(implicit theme: NineCardsTheme, uiContext: UiContext[_]) =\n    Tweak[W](_.reload(dockApp).run)\n\n  def daplReset() = Tweak[W](_.reset().run)\n\n}\n\nobject CollectionActionsPanelLayoutTweaks {\n  type W = CollectionActionsPanelLayout\n\n  def caplLoad(actions: Seq[CollectionActionItem])(implicit theme: NineCardsTheme) =\n    Tweak[W](_.load(actions).run)\n\n  def caplDragDispatcher(action: Int, x: Float, y: Float)(implicit theme: NineCardsTheme) =\n    Tweak[W](_.dragController(action, x, y))\n\n}\n\nobject TopBarLayoutTweaks {\n\n  type W = TopBarLayout\n\n  def tblInit(workSpaceType: WorkSpaceType)(\n      implicit theme: NineCardsTheme,\n      navigationJobs: NavigationJobs) =\n    Tweak[W](_.init(workSpaceType).run)\n\n  def tblReload(implicit theme: NineCardsTheme, navigationJobs: NavigationJobs) =\n    Tweak[W](_.populate.run)\n\n  def tblReloadMoment(moment: NineCardsMoment)(\n      implicit theme: NineCardsTheme,\n      launcherJobs: LauncherJobs,\n      navigationJobs: NavigationJobs) =\n    Tweak[W](_.reloadMoment(moment).run)\n\n  def tblReloadByType(workSpaceType: WorkSpaceType)(implicit contextWrapper: ContextWrapper) =\n    Tweak[W](_.reloadByType(workSpaceType).run)\n\n  def tblWeather(condition: ConditionWeather) =\n    Tweak[W](_.setWeather(condition).run)\n\n}\n\nobject AppsMomentLayoutTweaks {\n\n  type W = AppsMomentLayout\n\n  def amlPopulate(\n      moment: LauncherMoment)(implicit context: ActivityContextWrapper, theme: NineCardsTheme) =\n    Tweak[W](_.populate(moment).run)\n\n  def amlPaddingTopAndBottom(paddingTop: Int, paddingBottom: Int) =\n    Tweak[W](_.setPaddingTopAndBottom(paddingTop, paddingBottom).run)\n\n}\n\nobject EditHourMomentLayoutTweaks {\n  type W = EditHourMomentLayout\n\n  def ehmPopulate(\n      timeSlot: MomentTimeSlot,\n      position: Int,\n      onRemoveHour: (Int) => Unit,\n      onChangeFromHour: (Int, String) => Unit,\n      onChangeToHour: (Int, String) => Unit,\n      onSwapDays: (Int, Int) => Unit)(implicit theme: NineCardsTheme) =\n    Tweak[W](_.populate(\n      timeSlot,\n      position,\n      onRemoveHour,\n      onChangeFromHour,\n      onChangeToHour,\n      onSwapDays).run)\n\n}\n\nobject EditDeviceMomentLayoutTweaks {\n  type W = EditDeviceMomentLayout\n\n  def edmPopulate(deviceName: String, position: Int, onRemoveDevice: (Int) => Unit)(\n      implicit theme: NineCardsTheme) =\n    Tweak[W](_.populate(deviceName, position, onRemoveDevice).run)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/models/LauncherData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.models\n\nimport cards.nine.models.Collection\nimport cards.nine.models.types.NineCardsMoment\n\ncase class LauncherData(\n    workSpaceType: WorkSpaceType,\n    moment: Option[LauncherMoment] = None,\n    collections: Seq[Collection] = Seq.empty,\n    positionByType: Int = 0)\n\ncase class LauncherMoment(momentType: Option[NineCardsMoment], collection: Option[Collection])\n\nsealed trait WorkSpaceType {\n  val value: Int\n\n  def isMomentWorkSpace: Boolean = this == MomentWorkSpace\n}\n\ncase object MomentWorkSpace extends WorkSpaceType {\n  override val value: Int = 0\n}\n\ncase object CollectionsWorkSpace extends WorkSpaceType {\n  override val value: Int = 1\n}\n\nobject WorkSpaceType {\n  def apply(value: Int): WorkSpaceType = value match {\n    case MomentWorkSpace.value => MomentWorkSpace\n    case _                     => CollectionsWorkSpace\n  }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/preferences/AboutHeaderPreference.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.preferences\n\nimport android.content.Context\nimport android.preference.Preference\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport cards.nine.commons.javaNull\nimport cards.nine.app.ui.commons.SafeUi._\nimport com.fortysevendeg.ninecardslauncher.{R, TR}\nimport macroid.extras.TextViewTweaks._\nimport macroid._\nimport macroid.FullDsl._\n\nclass AboutHeaderPreference(context: Context, attrs: AttributeSet, defStyle: Int)\n    extends Preference(context, attrs, defStyle) {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attrs: AttributeSet) = this(context, attrs, 0)\n\n  implicit lazy val contextWrapper = ContextWrapper(context)\n\n  override def onCreateView(parent: ViewGroup): View = {\n    val aboutView = LayoutInflater\n      .from(context)\n      .inflate(R.layout.about_header_preference, javaNull)\n      .asInstanceOf[ViewGroup]\n\n    import cards.nine.app.ui.commons.ViewGroupFindViews._\n\n    val version = findView(TR.preference_about_name).run(aboutView)\n    val github  = findView(TR.preference_about_github).run(aboutView)\n\n    val info =\n      context.getPackageManager.getPackageInfo(context.getPackageName, 0)\n\n    ((version <~ tvText(info.versionName)) ~\n      (github <~ On.click(uiOpenUrlIntent(context.getString(R.string.nine_cards_github))))).run\n\n    aboutView\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/preferences/TeamPreference.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.preferences\n\nimport android.content.Context\nimport android.preference.Preference\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport android.widget.ImageView\nimport cards.nine.app.ui.commons.SafeUi._\nimport cards.nine.commons.javaNull\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport macroid.extras.UIActionsExtras._\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.{R, TR}\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.TextViewTweaks._\n\nimport scala.util.Random\n\nclass TeamPreference(context: Context, attrs: AttributeSet, defStyle: Int)\n    extends Preference(context, attrs, defStyle) {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attrs: AttributeSet) = this(context, attrs, 0)\n\n  implicit lazy val contextWrapper = ContextWrapper(context)\n\n  override def onCreateView(parent: ViewGroup): View = {\n    val teamView = LayoutInflater\n      .from(context)\n      .inflate(R.layout.about_team_preference, javaNull)\n      .asInstanceOf[ViewGroup]\n\n    import cards.nine.app.ui.commons.ViewGroupFindViews._\n\n    val teamLayout = findView(TR.preference_about_team).run(teamView)\n\n    import cards.nine.app.ui.commons.Team._\n\n    val padding = resGetDimensionPixelSize(R.dimen.padding_default)\n\n    val teamViews = Random.shuffle(team) map { person =>\n      (w[ImageView] <~\n        vWrapContent <~\n        vPaddings(padding) <~\n        ivSrc(person._2) <~\n        On.click(uiLongToast(resGetString(R.string.team_name, person._1)))).get\n    }\n\n    (teamLayout <~ vgAddViews(teamViews)).run\n\n    teamView\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/CollectionCheckBox.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets\n\nimport android.content.Context\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.OvalShape\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.LinearLayout\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.commons._\nimport cards.nine.models._\nimport cards.nine.models.types.theme.{CardLayoutBackgroundColor, ThemeDark, ThemeLight, ThemeType}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid._\n\nclass CollectionCheckBox(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends LinearLayout(context, attr, defStyleAttr)\n    with Contexts[View]\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  val checkKey = \"collection-check\"\n\n  val collectionKey = \"collection-moment\"\n\n  val paddingIcon = resGetDimensionPixelSize(R.dimen.padding_medium)\n\n  val paddingCheckbox = resGetDimensionPixelSize(R.dimen.card_padding_small)\n\n  val selectedColor = resGetColor(R.color.checkbox_selected)\n\n  val unselectedLightColor = resGetColor(R.color.checkbox_light_unselected)\n\n  val unselectedDarkColor = resGetColor(R.color.checkbox_dark_unselected)\n\n  def selectedDrawable(color: Int) = {\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.getPaint.setColor(color)\n    drawable\n  }\n\n  def unselectedDrawable(themeType: ThemeType) = {\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.getPaint.setColor(themeType match {\n      case ThemeLight => unselectedLightColor\n      case ThemeDark  => unselectedDarkColor\n    })\n    drawable\n  }\n\n  LayoutInflater.from(context).inflate(R.layout.collection_checkbox, this)\n\n  val collectionIcon = findView(TR.collection_icon)\n\n  val checkboxIconContent = findView(TR.subscriptions_item_content)\n\n  val checkboxIcon = findView(TR.collection_checkbox_icon)\n\n  (this <~ vAddField(checkKey, true)).run\n\n  val iconSelectedDrawable = PathMorphDrawable(\n    defaultIcon = IconTypes.CHECK,\n    defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_thin),\n    padding = resGetDimensionPixelSize(R.dimen.padding_select_icon))\n\n  def initialize(\n      icon: Int,\n      color: Int,\n      theme: NineCardsTheme,\n      defaultCheck: Boolean = true): Ui[Any] = {\n    (this <~ vAddField(collectionKey, icon)) ~\n      (collectionIcon <~\n        vBackground(selectedDrawable(color)) <~\n        vPaddings(paddingIcon) <~\n        ivSrc(icon)) ~\n      (checkboxIconContent <~\n        vBackground(selectedDrawable(theme.get(CardLayoutBackgroundColor)))) ~\n      (checkboxIcon <~\n        vBackground(selectedDrawable(selectedColor)) <~\n        ivSrc(iconSelectedDrawable)) ~\n      (if (defaultCheck) check(color) else uncheck(theme.parent))\n  }\n\n  def check(color: Int): Ui[Any] =\n    (this <~ vAddField(checkKey, true)) ~\n      (collectionIcon <~ vBackground(selectedDrawable(color))) ~\n      (checkboxIcon <~ vBackground(selectedDrawable(selectedColor)))\n\n  def uncheck(themeType: ThemeType): Ui[Any] =\n    (this <~ vAddField(checkKey, false)) ~\n      (collectionIcon <~ vBackground(unselectedDrawable(themeType))) ~\n      (checkboxIcon <~ vBackground(unselectedDrawable(themeType)))\n\n  def isCheck: Boolean = this.getField[Boolean](checkKey) exists (c => c)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/CollectionRecyclerView.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets\n\nimport android.content.Context\nimport android.support.v7.widget.{GridLayoutManager, RecyclerView}\nimport android.util.AttributeSet\nimport android.view.ViewGroup.LayoutParams\nimport android.view.animation.GridLayoutAnimationController.AnimationParameters\nimport android.view.{MotionEvent, View}\nimport cards.nine.commons._\nimport macroid.Contexts\n\nclass CollectionRecyclerView(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends RecyclerView(context, attr, defStyleAttr)\n    with Contexts[View] {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  var statuses = CollectionRecyclerStatuses()\n\n  override def dispatchTouchEvent(ev: MotionEvent): Boolean =\n    if (statuses.disableScroll) {\n      true\n    } else {\n      super.dispatchTouchEvent(ev)\n    }\n\n  override def attachLayoutAnimationParameters(\n      child: View,\n      params: LayoutParams,\n      index: Int,\n      count: Int): Unit =\n    (statuses.enableAnimation, Option(getLayoutManager)) match {\n      case (true, Some(layoutManager: GridLayoutManager)) =>\n        val animationParams = Option(params.layoutAnimationParameters) match {\n          case Some(animParams: AnimationParameters) => animParams\n          case _ =>\n            val animParams = new AnimationParameters()\n            params.layoutAnimationParameters = animParams\n            animParams\n        }\n        val columns = layoutManager.getSpanCount\n        animationParams.count = count\n        animationParams.index = index\n        animationParams.columnsCount = columns\n        animationParams.rowsCount = count / columns\n        val invertedIndex = count - 1 - index\n        animationParams.column = columns - 1 - (invertedIndex % columns)\n        animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns\n      case _ =>\n        super.attachLayoutAnimationParameters(child, params, index, count)\n    }\n\n}\n\ncase class CollectionRecyclerStatuses(\n    disableScroll: Boolean = false,\n    enableAnimation: Boolean = false)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/DrawerRecyclerView.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets\n\nimport android.content.Context\nimport android.support.v7.widget.{GridLayoutManager, RecyclerView}\nimport android.util.AttributeSet\nimport android.view.View\nimport android.view.ViewGroup.LayoutParams\nimport android.view.animation.GridLayoutAnimationController.AnimationParameters\nimport cards.nine.commons._\nimport macroid._\n\nclass DrawerRecyclerView(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends RecyclerView(context, attr, defStyleAttr)\n    with Contexts[View] { self =>\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  var statuses = DrawerRecyclerStatuses()\n\n  override def attachLayoutAnimationParameters(\n      child: View,\n      params: LayoutParams,\n      index: Int,\n      count: Int): Unit =\n    Option(getLayoutManager) match {\n      case (Some(layoutManager: GridLayoutManager)) =>\n        val animationParams = Option(params.layoutAnimationParameters) match {\n          case Some(animParams: AnimationParameters) => animParams\n          case _ =>\n            val animParams = new AnimationParameters()\n            params.layoutAnimationParameters = animParams\n            animParams\n        }\n        val columns = layoutManager.getSpanCount\n        animationParams.count = count\n        animationParams.index = index\n        animationParams.columnsCount = columns\n        animationParams.rowsCount = count / columns\n        val invertedIndex = count - 1 - index\n        animationParams.column = columns - 1 - (invertedIndex % columns)\n        animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns\n      case _ =>\n        super.attachLayoutAnimationParameters(child, params, index, count)\n    }\n\n}\n\ncase class DrawerRecyclerStatuses(contentView: ContentView = AppsView)\n\nsealed trait ContentView\n\ncase object AppsView extends ContentView\n\ncase object ContactView extends ContentView\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/LauncherNoConfiguredWidgetView.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets\n\nimport android.content.Context\nimport android.graphics.Color\nimport android.view.Gravity\nimport android.widget.FrameLayout.LayoutParams\nimport android.widget.{FrameLayout, ImageView}\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.{GenericUiContext, UiContext}\nimport cards.nine.app.ui.launcher.jobs.WidgetsJobs\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.models.Widget\nimport macroid.extras.FrameLayoutTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\n\ncase class LauncherNoConfiguredWidgetView(id: Int, wCell: Int, hCell: Int, widget: Widget)(\n    implicit contextWrapper: ContextWrapper,\n    widgetJobs: WidgetsJobs)\n    extends FrameLayout(contextWrapper.bestAvailable) {\n\n  implicit lazy val uiContext: UiContext[Context] = GenericUiContext(getContext)\n\n  val letter = \"W\"\n\n  val paddingDefault = resGetDimensionPixelSize(R.dimen.padding_default)\n\n  val stroke = resGetDimensionPixelSize(R.dimen.stroke_thin)\n\n  val icon = (w[ImageView] <~\n    vWrapContent <~\n    ivSrcByPackageName(Some(widget.packageName), letter) <~\n    flLayoutGravity(Gravity.CENTER)).get\n\n  (this <~\n    vBackgroundColor(Color.GRAY.alpha(.5f)) <~\n    vgAddView(icon) <~\n    On.click(Ui(widgetJobs.hostNoConfiguredWidget(widget).resolveAsync()))).run\n\n  def addView(): Tweak[FrameLayout] =\n    vgAddView(this, createParams())\n\n  private[this] def createParams(): LayoutParams = {\n    val (width, height) =\n      (widget.area.spanX * wCell, widget.area.spanY * hCell)\n    val (startX, startY) =\n      (widget.area.startX * wCell, widget.area.startY * hCell)\n    val params = new LayoutParams(width + stroke, height + stroke)\n    val left   = paddingDefault + startX\n    val top    = paddingDefault + startY\n    params.setMargins(left, top, paddingDefault, paddingDefault)\n    params\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/LauncherWidgetResizeFrame.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets\n\nimport android.graphics.Rect\nimport android.support.v4.view.MotionEventCompat\nimport android.view.MotionEvent.{ACTION_CANCEL, ACTION_DOWN, ACTION_MOVE, ACTION_UP}\nimport android.view.{Gravity, MotionEvent}\nimport android.widget.FrameLayout.LayoutParams\nimport android.widget.{FrameLayout, ImageView}\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.commons.ops.WidgetsOps._\nimport cards.nine.models.WidgetArea\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid.extras.FrameLayoutTweaks._\nimport macroid.extras.ImageViewTweaks.ivSrc\nimport macroid.extras.ResourcesExtras.resGetDimensionPixelSize\nimport macroid.extras.ViewGroupTweaks.vgAddView\nimport macroid.extras.ViewTweaks.{vBackground, _}\nimport macroid.{ContextWrapper, Ui, _}\n\nclass LauncherWidgetResizeFrame(\n    widgetArea: WidgetArea,\n    widthCell: Int,\n    heightCell: Int,\n    onResizeChangeArea: (WidgetArea) => Boolean,\n    onResizeFinished: () => Unit)(implicit contextWrapper: ContextWrapper)\n    extends FrameLayout(contextWrapper.bestAvailable) {\n\n  val resizeHandleType = \"resize-handle\"\n\n  val paddingDefault = resGetDimensionPixelSize(R.dimen.padding_default)\n\n  val stroke = resGetDimensionPixelSize(R.dimen.stroke_thin)\n\n  lazy val resizeHandleSize = paddingDefault * 2\n\n  case class LauncherResizeFrameStatuses(\n      area: WidgetArea = widgetArea,\n      draggingResizeType: Option[ResizeType] = None,\n      draggingArea: WidgetArea = widgetArea,\n      startDragX: Int = 0,\n      startDragY: Int = 0) {\n\n    def start(x: Int, y: Int): LauncherResizeFrameStatuses =\n      copy(\n        draggingResizeType = getResizeType(x, y),\n        startDragX = x,\n        startDragY = y,\n        draggingArea = area)\n\n    def calculateNewArea(x: Int, y: Int, resizeType: ResizeType): Option[WidgetArea] = {\n      val horizontal = (x - startDragX) / widthCell\n      val vertical   = (y - startDragY) / heightCell\n      val newArea = resizeType match {\n        case TopResize =>\n          area.copy(startY = area.startY + vertical, spanY = area.spanY - vertical)\n        case BottomResize => area.copy(spanY = area.spanY + vertical)\n        case LeftResize =>\n          area.copy(startX = area.startX + horizontal, spanX = area.spanX - horizontal)\n        case RightResize => area.copy(spanX = area.spanX + horizontal)\n      }\n      if (newArea != draggingArea && newArea.isValid(columns, rows)) {\n        Option(newArea)\n      } else {\n        None\n      }\n    }\n\n  }\n\n  var frameStatuses = LauncherResizeFrameStatuses()\n\n  private[this] val frame = {\n\n    def addResizeHandle() = {\n      val paramsLeft =\n        new LayoutParams(resizeHandleSize, resizeHandleSize, Gravity.CENTER_VERTICAL)\n      val left =\n        (w[ImageView] <~ vSetType(resizeHandleType) <~ ivSrc(R.drawable.mark_widget_resizing)).get\n      val paramsRight = new LayoutParams(\n        resizeHandleSize,\n        resizeHandleSize,\n        Gravity.CENTER_VERTICAL | Gravity.RIGHT)\n      val right =\n        (w[ImageView] <~ vSetType(resizeHandleType) <~ ivSrc(R.drawable.mark_widget_resizing)).get\n      val paramsTop =\n        new LayoutParams(resizeHandleSize, resizeHandleSize, Gravity.CENTER_HORIZONTAL)\n      val top =\n        (w[ImageView] <~ vSetType(resizeHandleType) <~ ivSrc(R.drawable.mark_widget_resizing)).get\n      val paramsBottom = new LayoutParams(\n        resizeHandleSize,\n        resizeHandleSize,\n        Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM)\n      val bottom =\n        (w[ImageView] <~ vSetType(resizeHandleType) <~ ivSrc(R.drawable.mark_widget_resizing)).get\n      vgAddView(left, paramsLeft) +\n        vgAddView(right, paramsRight) +\n        vgAddView(top, paramsTop) +\n        vgAddView(bottom, paramsBottom)\n    }\n    val content = l[FrameLayout](\n      w[ImageView] <~\n        vMatchParent <~\n        flLayoutMargin(\n          marginLeft = paddingDefault,\n          marginTop = paddingDefault,\n          marginRight = paddingDefault,\n          marginBottom = paddingDefault) <~\n        vBackground(R.drawable.stroke_widget_selected)\n    ).get\n    (content <~ addResizeHandle()).run\n    content\n  }\n\n  ((this <~ vgAddView(frame)) ~ hideResizeHandle()).run\n\n  override def onInterceptTouchEvent(event: MotionEvent): Boolean = {\n    val action = MotionEventCompat.getActionMasked(event)\n    val x      = MotionEventCompat.getX(event, 0).toInt\n    val y      = MotionEventCompat.getY(event, 0).toInt\n\n    action match {\n      case ACTION_DOWN =>\n        frameStatuses = frameStatuses.start(x.toInt, y.toInt)\n        false\n      case ACTION_MOVE =>\n        requestDisallowInterceptTouchEvent(true)\n        updateFrame(x, y).run\n        changeAreaIfNecessary(x, y)\n        frameStatuses.draggingResizeType.isDefined\n      case ACTION_UP | ACTION_CANCEL =>\n        if (frameStatuses.draggingResizeType.isEmpty) {\n          onResizeFinished()\n        } else {\n          updateView(frameStatuses.draggingArea).run\n        }\n        frameStatuses = frameStatuses.copy(draggingResizeType = None)\n        false\n      case _ => false\n    }\n  }\n\n  override def onTouchEvent(event: MotionEvent): Boolean = {\n    val action = MotionEventCompat.getActionMasked(event)\n    val x      = MotionEventCompat.getX(event, 0).toInt\n    val y      = MotionEventCompat.getY(event, 0).toInt\n\n    action match {\n      case ACTION_DOWN =>\n        Option(getParent) foreach (_.requestDisallowInterceptTouchEvent(true))\n        frameStatuses = frameStatuses.start(x.toInt, y.toInt)\n      case ACTION_MOVE =>\n        requestDisallowInterceptTouchEvent(true)\n        updateFrame(x, y).run\n        changeAreaIfNecessary(x, y)\n      case ACTION_UP | ACTION_CANCEL =>\n        if (frameStatuses.draggingResizeType.isEmpty) {\n          onResizeFinished()\n        } else {\n          updateView(frameStatuses.draggingArea).run\n        }\n        frameStatuses = frameStatuses.copy(draggingResizeType = None)\n      case _ =>\n    }\n    true\n  }\n\n  def getResizeType(x: Int, y: Int): Option[ResizeType] = {\n    val rect = getRect\n    if (x >= rect.left - paddingDefault && x <= rect.left + resizeHandleSize + paddingDefault) {\n      Option(LeftResize)\n    } else if (x >= rect.right - paddingDefault - resizeHandleSize && x <= rect.right + paddingDefault) {\n      Option(RightResize)\n    } else if (y >= rect.top - paddingDefault && y <= rect.top + resizeHandleSize + paddingDefault) {\n      Option(TopResize)\n    } else if (y >= rect.bottom - paddingDefault - resizeHandleSize && y <= rect.bottom + paddingDefault) {\n      Option(BottomResize)\n    } else {\n      None\n    }\n  }\n\n  def activeResize(): Ui[Any] = showResizeHandle()\n\n  def updateView(area: WidgetArea): Ui[Any] = {\n    frameStatuses = frameStatuses.copy(area = area)\n    Ui(frame.setLayoutParams(createParams()))\n  }\n\n  private[this] def createParams(): LayoutParams = {\n    val rect   = getRect\n    val params = new LayoutParams(rect.width(), rect.height())\n    params.setMargins(rect.left, rect.top, 0, 0)\n    params\n  }\n\n  private[this] def updateFrame(x: Int, y: Int): Ui[Any] = {\n    val params = createParams()\n    frameStatuses.draggingResizeType match {\n      case Some(LeftResize) =>\n        val left = x - frameStatuses.startDragX\n        params.leftMargin = params.leftMargin + left\n        params.width = params.width - left\n      case Some(RightResize) =>\n        val right = x - frameStatuses.startDragX\n        params.width = params.width + right\n      case Some(TopResize) =>\n        val top = y - frameStatuses.startDragY\n        params.topMargin = params.topMargin + top\n        params.height = params.height - top\n      case Some(BottomResize) =>\n        val bottom = y - frameStatuses.startDragY\n        params.height = params.height + bottom\n      case _ =>\n    }\n    Ui(frame.setLayoutParams(params))\n  }\n\n  private[this] def changeAreaIfNecessary(x: Int, y: Int) = {\n    for {\n      resizeType <- frameStatuses.draggingResizeType\n      area       <- frameStatuses.calculateNewArea(x, y, resizeType)\n    } yield {\n      if (onResizeChangeArea(area)) {\n        frameStatuses = frameStatuses.copy(draggingArea = area)\n      }\n    }\n  }\n\n  private[this] def getRect: Rect = {\n    val cell = Cell(frameStatuses.area.spanX, frameStatuses.area.spanY, widthCell, heightCell)\n    val (width, height) =\n      cell.getSize(frameStatuses.area.spanX, frameStatuses.area.spanY)\n    val (startX, startY) =\n      cell.getSize(frameStatuses.area.startX, frameStatuses.area.startY)\n    new Rect(\n      startX,\n      startY,\n      startX + width + resizeHandleSize + stroke,\n      startY + height + resizeHandleSize + stroke)\n  }\n\n  private[this] def showResizeHandle() = this <~ Transformer {\n    case i: ImageView if i.isType(resizeHandleType) => i <~ vVisible\n  }\n\n  private[this] def hideResizeHandle() = this <~ Transformer {\n    case i: ImageView if i.isType(resizeHandleType) => i <~ vInvisible\n  }\n\n}\n\nsealed trait ResizeType\n\ncase object TopResize extends ResizeType\ncase object BottomResize extends ResizeType\ncase object LeftResize extends ResizeType\ncase object RightResize extends ResizeType\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/LauncherWidgetView.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets\n\nimport android.appwidget.AppWidgetHostView\nimport android.view.MotionEvent._\nimport android.view.View.{OnLongClickListener, OnTouchListener}\nimport android.view.{MotionEvent, View, ViewConfiguration}\nimport android.widget.FrameLayout\nimport android.widget.FrameLayout.LayoutParams\nimport cards.nine.app.ui.commons.CommonsTweak.vStartDrag\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.commons.ops.WidgetsOps.Cell\nimport cards.nine.app.ui.launcher.EditWidgetsMode\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.jobs.WidgetsJobs\nimport cards.nine.app.ui.launcher.types.{ReorderWidget, WidgetShadowBuilder}\nimport cards.nine.commons._\nimport cards.nine.models.Widget\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\n\nclass LauncherWidgetView(initialWidget: Widget, widgetView: AppWidgetHostView)(\n    implicit contextWrapper: ContextWrapper,\n    widgetJobs: WidgetsJobs)\n    extends FrameLayout(contextWrapper.bestAvailable) { self =>\n\n  val paddingDefault = resGetDimensionPixelSize(R.dimen.padding_default)\n\n  val stroke = resGetDimensionPixelSize(R.dimen.stroke_thin)\n\n  lazy val slop = ViewConfiguration.get(getContext).getScaledTouchSlop\n\n  val longPressHelper =\n    new CheckLongPressHelper(widgetView, new OnLongClickListener {\n      override def onLongClick(v: View): Boolean = {\n        widgetJobs.openModeEditWidgets(widgetStatuses.widget.id).resolveAsync()\n        (self <~\n          vStartDrag(\n            ReorderWidget,\n            new WidgetShadowBuilder(widgetView),\n            Option(widgetStatuses.widget.id.toString),\n            None)).run\n        true\n      }\n    })\n\n  case class LauncherWidgetViewStatuses(\n      widget: Widget = initialWidget,\n      lastX: Float = 0,\n      lastY: Float = 0)\n\n  var widgetStatuses = LauncherWidgetViewStatuses()\n\n  override def onInterceptTouchEvent(event: MotionEvent): Boolean = {\n    (event.getAction, longPressHelper.hasPerformedLongPress, isMoving(event.getX, event.getY)) match {\n      case (ACTION_DOWN, _, _) =>\n        longPressHelper.cancelLongPress()\n        longPressHelper.postCheckForLongPress()\n        widgetStatuses = widgetStatuses.copy(lastX = event.getX, lastY = event.getY)\n        false\n      case (_, true, _) =>\n        longPressHelper.cancelLongPress()\n        true\n      case (ACTION_UP | ACTION_CANCEL, _, _) =>\n        longPressHelper.cancelLongPress()\n        false\n      case (ACTION_MOVE, _, true) =>\n        longPressHelper.cancelLongPress()\n        false\n      case _ => false\n    }\n  }\n\n  override def onTouchEvent(event: MotionEvent): Boolean = {\n    (event.getAction, isMoving(event.getX, event.getY)) match {\n      case (ACTION_UP | ACTION_CANCEL, _) =>\n        longPressHelper.cancelLongPress()\n      case (ACTION_DOWN, _) =>\n        longPressHelper.cancelLongPress()\n        widgetStatuses = widgetStatuses.copy(lastX = event.getX, lastY = event.getY)\n      case (ACTION_MOVE, true) =>\n        longPressHelper.cancelLongPress()\n      case _ =>\n    }\n    true\n  }\n\n  val viewBlockTouch = w[FrameLayout].get\n  viewBlockTouch.setOnTouchListener(new OnTouchListener {\n    override def onTouch(v: View, event: MotionEvent): Boolean = {\n      event.getAction match {\n        case ACTION_DOWN =>\n          statuses = statuses.copy(touchingWidget = true)\n          if (statuses.mode == EditWidgetsMode)\n            widgetJobs.closeModeEditWidgets().resolveAsync()\n          false\n        case _ => false\n      }\n    }\n  })\n\n  (this <~ vgAddViews(Seq(widgetView, viewBlockTouch))).run\n\n  def activeSelected(): Ui[Any] = this <~ Transformer {\n    case v: AppWidgetHostView => v <~ vInvisible\n  }\n\n  def deactivateSelected(): Ui[Any] = this <~ Transformer {\n    case v: AppWidgetHostView => v <~ vVisible\n  }\n\n  def activeResize(): Ui[Any] = this <~ Transformer {\n    case v: AppWidgetHostView => v <~ vVisible\n  }\n\n  def addView(cell: Cell, widget: Widget): Tweak[FrameLayout] = {\n    updateWidgetSize(cell, widget)\n    vgAddView(this, createParams(cell, widget))\n  }\n\n  def updateView(widget: Widget): Ui[Any] =\n    this.getField[Cell](LauncherWidgetView.cellKey) match {\n      case Some(cell) =>\n        Ui {\n          widgetStatuses = widgetStatuses.copy(widget = widget)\n          updateWidgetSize(cell, widget)\n          setLayoutParams(createParams(cell, widget))\n        }\n      case _ => Ui.nop\n    }\n\n  private[this] def createParams(cell: Cell, widget: Widget): LayoutParams = {\n    val (width, height)  = cell.getSize(widget.area.spanX, widget.area.spanY)\n    val (startX, startY) = cell.getSize(widget.area.startX, widget.area.startY)\n    val params           = new LayoutParams(width + stroke, height + stroke)\n    val left             = paddingDefault + startX\n    val top              = paddingDefault + startY\n    params.setMargins(left, top, paddingDefault, paddingDefault)\n    params\n  }\n\n  private[this] def updateWidgetSize(cell: Cell, widget: Widget): Unit = {\n    val density: Float = getResources.getDisplayMetrics.density\n    val (width, height) =\n      cell.getSize(widget.area.spanX, widget.area.spanY) match {\n        case (w, h) =>\n          (((w - paddingDefault) / density).toInt, ((h - paddingDefault) / density).toInt)\n      }\n    widgetView.updateAppWidgetSize(javaNull, width, height, width, height)\n    widgetView.requestLayout()\n  }\n\n  private[this] def isMoving(localX: Float, localY: Float): Boolean = {\n    val moveX = math.abs(localX - widgetStatuses.lastX)\n    val moveY = math.abs(localY - widgetStatuses.lastY)\n    (moveX > slop) || (moveY > slop)\n  }\n\n  class CheckLongPressHelper(view: View, listener: View.OnLongClickListener) {\n\n    case class CheckStatuses(\n        hasPerformedLongPress: Boolean = false,\n        pendingCheckForLongPress: Option[CheckForLongPress] = None)\n\n    private[this] val longPressTimeout = 300\n\n    private[this] var checkStatus = CheckStatuses()\n\n    class CheckForLongPress extends Runnable {\n      def run(): Unit =\n        (Option(view.getParent), view.hasWindowFocus, checkStatus.hasPerformedLongPress) match {\n          case (Some(_), true, false) =>\n            if (listener.onLongClick(view)) {\n              view.setPressed(false)\n              checkStatus = checkStatus.copy(hasPerformedLongPress = true)\n            }\n          case _ =>\n        }\n    }\n\n    def postCheckForLongPress(): Unit = {\n      checkStatus = checkStatus.copy(hasPerformedLongPress = false)\n      if (checkStatus.pendingCheckForLongPress.isEmpty)\n        checkStatus = checkStatus.copy(pendingCheckForLongPress = Option(new CheckForLongPress()))\n      checkStatus.pendingCheckForLongPress foreach (view.postDelayed(_, longPressTimeout))\n    }\n\n    def cancelLongPress(): Unit = {\n      checkStatus = checkStatus.copy(hasPerformedLongPress = false)\n      checkStatus.pendingCheckForLongPress foreach { longPress =>\n        view.removeCallbacks(longPress)\n        checkStatus = checkStatus.copy(pendingCheckForLongPress = None)\n      }\n    }\n\n    def hasPerformedLongPress: Boolean = checkStatus.hasPerformedLongPress\n\n  }\n\n}\n\nobject LauncherWidgetView {\n  // TODO We should remove that and store only width and height content in statuses\n  val cellKey   = \"cell\"\n  val widgetKey = \"widget\"\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/ScrollingLinearLayoutManager.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets\n\nimport android.graphics.PointF\nimport android.support.v7.widget.LinearLayoutManager._\nimport android.support.v7.widget.LinearSmoothScroller._\nimport android.support.v7.widget.RecyclerView.State\nimport android.support.v7.widget.{\n  GridLayoutManager,\n  LinearLayoutManager,\n  LinearSmoothScroller,\n  RecyclerView\n}\nimport android.util.DisplayMetrics\nimport macroid.ContextWrapper\n\nclass ScrollingLinearLayoutManager(columns: Int)(implicit contextWrapper: ContextWrapper)\n    extends GridLayoutManager(contextWrapper.application, columns) { self =>\n\n  var blockScroll = false\n\n  val maxPositions = 50f\n\n  val minSpeedFactor = 5f\n\n  val maxSpeedFactor = 25f\n\n  val varSpeedFactor = maxSpeedFactor - minSpeedFactor\n\n  override def smoothScrollToPosition(\n      recyclerView: RecyclerView,\n      state: State,\n      position: Int): Unit = {\n    val steps = math.min(recyclerView.getLayoutManager match {\n      case lm: LinearLayoutManager =>\n        math.abs(position - lm.findFirstVisibleItemPosition()).toFloat\n      case _ => 1\n    }, maxPositions)\n    val speedFactor: Float = (varSpeedFactor - (steps * varSpeedFactor / maxPositions)) + minSpeedFactor\n    val smoothScroller     = new TopSmoothScroller(recyclerView, speedFactor)\n    smoothScroller.setTargetPosition(position)\n    startSmoothScroll(smoothScroller)\n  }\n\n  override def canScrollVertically: Boolean =\n    if (blockScroll) false else getOrientation == VERTICAL\n\n  class TopSmoothScroller(recyclerView: RecyclerView, speedFactor: Float)\n      extends LinearSmoothScroller(recyclerView.getContext) {\n\n    override def computeScrollVectorForPosition(targetPosition: Int): PointF =\n      self.computeScrollVectorForPosition(targetPosition)\n\n    protected override def getVerticalSnapPreference: Int = SNAP_TO_START\n\n    protected override def calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float =\n      speedFactor / displayMetrics.densityDpi.toFloat\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/TintableButton.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets\n\nimport android.content.Context\nimport android.graphics.PorterDuff.Mode\nimport android.graphics.{Color, PorterDuffColorFilter, Rect}\nimport android.support.v4.view.MotionEventCompat\nimport android.util.AttributeSet\nimport android.view.MotionEvent\nimport android.view.MotionEvent._\nimport android.widget.Button\nimport cards.nine.commons._\n\nclass TintableButton(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends Button(context, attr, defStyleAttr) {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  var defaultColor = Color.WHITE\n\n  var pressedColor = Color.WHITE\n\n  var outSide = false\n\n  def setTint(color: Int) = {\n    getCompoundDrawables.toList flatMap (Option(_)) foreach (_.setColorFilter(\n      new PorterDuffColorFilter(color, Mode.MULTIPLY)))\n    setTextColor(color)\n    invalidate()\n  }\n\n  def setPressedColor() = setTint(pressedColor)\n\n  def setDefaultColor() = setTint(defaultColor)\n\n  override def onTouchEvent(event: MotionEvent): Boolean = {\n    val action = MotionEventCompat.getActionMasked(event)\n    action match {\n      case ACTION_DOWN =>\n        setTint(pressedColor)\n        outSide = false\n      case ACTION_MOVE =>\n        val rect = new Rect(getLeft, getTop, getRight, getBottom)\n        val aux =\n          rect.contains(getLeft + event.getX.toInt, getTop + event.getY.toInt)\n        if (aux != outSide) {\n          outSide = aux\n          if (outSide) setTint(pressedColor) else setTint(defaultColor)\n        }\n      case ACTION_UP | ACTION_CANCEL => setTint(defaultColor)\n      case _                         =>\n    }\n    super.onTouchEvent(event)\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/TintableImageView.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets\n\nimport android.content.Context\nimport android.graphics.PorterDuff.Mode\nimport android.graphics.{Color, PorterDuffColorFilter, Rect}\nimport android.support.v4.view.MotionEventCompat\nimport android.util.AttributeSet\nimport android.view.MotionEvent\nimport android.view.MotionEvent._\nimport android.widget.ImageView\nimport cards.nine.commons._\n\nclass TintableImageView(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends ImageView(context, attr, defStyleAttr) {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  var defaultColor = Color.WHITE\n\n  var pressedColor = Color.WHITE\n\n  var outSide = false\n\n  def setTint(color: Int) =\n    setColorFilter(new PorterDuffColorFilter(color, Mode.MULTIPLY))\n\n  override def onTouchEvent(event: MotionEvent): Boolean = {\n    val action = MotionEventCompat.getActionMasked(event)\n    action match {\n      case ACTION_DOWN => setTint(pressedColor); outSide = false\n      case ACTION_MOVE => {\n        val rect = new Rect(getLeft, getTop, getRight, getBottom)\n        val aux =\n          rect.contains(getLeft + event.getX.toInt, getTop + event.getY.toInt)\n        if (aux != outSide) {\n          outSide = aux\n          if (outSide) setTint(pressedColor) else setTint(defaultColor)\n        }\n      }\n      case ACTION_UP | ACTION_CANCEL => setTint(defaultColor)\n      case _                         =>\n    }\n    super.onTouchEvent(event)\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/WizardCheckBox.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets\n\nimport android.content.Context\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.OvalShape\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.LinearLayout\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport macroid.extras.UIActionsExtras._\nimport cards.nine.app.ui.commons.ops.NineCardsCategoryOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.commons._\nimport cards.nine.models.PackagesByCategory\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid._\n\nclass WizardCheckBox(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends LinearLayout(context, attr, defStyleAttr)\n    with Contexts[View]\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  val checkKey = \"widget-check\"\n\n  val dataKey = \"widget-data\"\n\n  val sizeIconTitle = resGetDimensionPixelSize(R.dimen.wizard_size_checkbox_title)\n\n  val sizeIconCollection = resGetDimensionPixelSize(R.dimen.wizard_size_checkbox_collection)\n\n  val paddingIcon = resGetDimensionPixelSize(R.dimen.padding_default)\n\n  val selectedColor = resGetColor(R.color.wizard_new_conf_accent_1)\n\n  val unselectedColor = resGetColor(R.color.wizard_checkbox_unselected)\n\n  val selectedDrawable = {\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.getPaint.setColor(selectedColor)\n    drawable\n  }\n\n  val unselectedDrawable = {\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.getPaint.setColor(unselectedColor)\n    drawable\n  }\n\n  LayoutInflater.from(context).inflate(R.layout.wizard_checkbox, this)\n\n  val icon = findView(TR.wizard_check_icon)\n\n  val text = findView(TR.wizard_check_text)\n\n  (this <~ vAddField(checkKey, true)).run\n\n  val iconSelectedDrawable = PathMorphDrawable(\n    defaultIcon = IconTypes.CHECK,\n    defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_thin),\n    padding = resGetDimensionPixelSize(R.dimen.padding_small))\n\n  def initialize(resText: Int, defaultCheck: Boolean = true): Ui[Any] =\n    (icon <~\n      vResize(sizeIconTitle, sizeIconTitle) <~\n      ivSrc(iconSelectedDrawable)) ~\n      (text <~\n        tvText(resText) <~\n        tvSizeResource(R.dimen.text_large)) ~\n      (if (defaultCheck) check() else uncheck())\n\n  def initializeCollection(\n      packagesByCategory: PackagesByCategory,\n      defaultCheck: Boolean = true): Ui[Any] = {\n    val nineCardCategory = packagesByCategory.category\n    val title = resGetString(\n      R.string.wizard_new_conf_collection_name_step_1,\n      nineCardCategory.getName,\n      packagesByCategory.packages.length.toString)\n    (this <~ vAddField(dataKey, packagesByCategory)) ~\n      (icon <~\n        vResize(sizeIconCollection, sizeIconCollection) <~\n        vPaddings(paddingIcon) <~\n        ivSrc(nineCardCategory.getIconCollectionDetail)) ~\n      (text <~\n        tvText(title) <~\n        tvSizeResource(R.dimen.text_xlarge)) ~\n      (if (defaultCheck) check() else uncheck())\n  }\n\n  def check(): Ui[Any] =\n    (this <~ vAddField(checkKey, true)) ~\n      (icon <~ vBackground(selectedDrawable)) ~\n      (text <~ tvColorResource(R.color.wizard_text_title))\n\n  def uncheck(): Ui[Any] =\n    (this <~ vAddField(checkKey, false)) ~\n      (icon <~ vBackground(unselectedDrawable)) ~\n      (text <~ tvColorResource(R.color.wizard_checkbox_unselected))\n\n  def swap(): Ui[Any] = this.getField[Boolean](checkKey) match {\n    case Some(true)  => uncheck()\n    case Some(false) => check()\n    case _           => Ui.nop\n  }\n\n  def isCheck: Boolean = this.getField[Boolean](checkKey) exists (c => c)\n\n  def getData: Option[PackagesByCategory] =\n    this.getField[PackagesByCategory](dataKey)\n\n  def getDataIfSelected: Option[PackagesByCategory] =\n    if (isCheck) this.getField[PackagesByCategory](dataKey) else None\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/WizardMomentCheckBox.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets\n\nimport android.content.Context\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.OvalShape\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.LinearLayout\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.ops.NineCardsMomentOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.commons._\nimport cards.nine.models.types.NineCardsMoment\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid._\n\nclass WizardMomentCheckBox(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends LinearLayout(context, attr, defStyleAttr)\n    with Contexts[View]\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  val checkKey = \"widget-check\"\n\n  val momentKey = \"widget-moment\"\n\n  val paddingIcon = resGetDimensionPixelSize(R.dimen.padding_default)\n\n  val selectedColor = resGetColor(R.color.wizard_new_conf_accent_3)\n\n  val tagSelectedColor = resGetColor(R.color.checkbox_selected)\n\n  val tagBackgroundColor = resGetColor(R.color.background_app)\n\n  val unselectedColor = resGetColor(R.color.wizard_checkbox_unselected)\n\n  val selectedDrawable = {\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.getPaint.setColor(selectedColor)\n    drawable\n  }\n\n  val unselectedDrawable = {\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.getPaint.setColor(unselectedColor)\n    drawable\n  }\n\n  val tagSelectedDrawable = {\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.getPaint.setColor(tagSelectedColor)\n    drawable\n  }\n\n  val tagUnselectedDrawable = {\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.getPaint.setColor(unselectedColor)\n    drawable\n  }\n\n  val tagBackgroundDrawable = {\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.getPaint.setColor(tagBackgroundColor)\n    drawable\n  }\n\n  LayoutInflater.from(context).inflate(R.layout.wizard_moment_checkbox, this)\n\n  val iconContent = findView(TR.wizard_moment_check_content)\n\n  val icon = findView(TR.wizard_moment_check_icon)\n\n  val text = findView(TR.wizard_moment_check_name)\n\n  val tag = findView(TR.wizard_moment_check_tag)\n\n  val tagContent = findView(TR.wizard_moment_check_tag_content)\n\n  (this <~ vAddField(checkKey, true)).run\n\n  val iconSelectedDrawable = PathMorphDrawable(\n    defaultIcon = IconTypes.CHECK,\n    defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_thin),\n    padding = resGetDimensionPixelSize(R.dimen.padding_small))\n\n  def initialize(moment: NineCardsMoment, defaultCheck: Boolean = true): Ui[Any] = {\n    (this <~ vAddField(momentKey, moment)) ~\n      (icon <~\n        vPaddings(paddingIcon) <~\n        ivSrc(moment.getIconCollectionDetail)) ~\n      (tag <~\n        ivSrc(iconSelectedDrawable)) ~\n      (tagContent <~\n        vBackground(tagBackgroundDrawable)) ~\n      (text <~\n        tvText(moment.getName) <~\n        tvSizeResource(R.dimen.text_xlarge)) ~\n      (if (defaultCheck) check() else uncheck())\n  }\n\n  def check(): Ui[Any] =\n    (this <~ vAddField(checkKey, true)) ~\n      (iconContent <~ vBackground(selectedDrawable)) ~\n      (tag <~ vBackground(tagSelectedDrawable)) ~\n      (text <~ tvColorResource(R.color.wizard_text_title))\n\n  def uncheck(): Ui[Any] =\n    (this <~ vAddField(checkKey, false)) ~\n      (iconContent <~ vBackground(unselectedDrawable)) ~\n      (tag <~ vBackground(tagUnselectedDrawable)) ~\n      (text <~ tvColorResource(R.color.wizard_checkbox_unselected))\n\n  def swap(): Ui[Any] = this.getField[Boolean](checkKey) match {\n    case Some(true)  => uncheck()\n    case Some(false) => check()\n    case _           => Ui.nop\n  }\n\n  def isCheck: Boolean = this.getField[Boolean](checkKey) exists (c => c)\n\n  def getMomentIfSelected: Option[NineCardsMoment] =\n    if (isCheck) this.getField[NineCardsMoment](momentKey) else None\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/WizardWifiCheckBox.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets\n\nimport android.content.Context\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.OvalShape\nimport android.util.AttributeSet\nimport android.view.{LayoutInflater, View}\nimport android.widget.LinearLayout\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.ops.NineCardsMomentOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.commons._\nimport cards.nine.models.types.NineCardsMoment\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nclass WizardWifiCheckBox(context: Context, attr: AttributeSet, defStyleAttr: Int)\n    extends LinearLayout(context, attr, defStyleAttr)\n    with Contexts[View]\n    with TypedFindView {\n\n  def this(context: Context) = this(context, javaNull, 0)\n\n  def this(context: Context, attr: AttributeSet) = this(context, attr, 0)\n\n  val checkKey = \"widget-check\"\n\n  val momentKey = \"widget-moment\"\n\n  val wifiNameKey = \"widget-wifi-name\"\n\n  val paddingIcon = resGetDimensionPixelSize(R.dimen.padding_default)\n\n  val selectedColor = resGetColor(R.color.wizard_new_conf_accent_2)\n\n  val unselectedColor = resGetColor(R.color.wizard_checkbox_unselected)\n\n  val selectedDrawable = {\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.getPaint.setColor(selectedColor)\n    drawable\n  }\n\n  val unselectedDrawable = {\n    val drawable = new ShapeDrawable(new OvalShape)\n    drawable.getPaint.setColor(unselectedColor)\n    drawable\n  }\n\n  LayoutInflater.from(context).inflate(R.layout.wizard_wifi_checkbox, this)\n\n  val icon = findView(TR.wizard_wifi_check_icon)\n\n  val name = findView(TR.wizard_wifi_check_name)\n\n  val textConnected = findView(TR.wizard_wifi_check_connected)\n\n  val wifiAction = findView(TR.wizard_wifi_check_wifi_action)\n\n  (this <~ vAddField(checkKey, true)).run\n\n  def initialize(\n      moment: NineCardsMoment,\n      onWifiClick: () => Unit,\n      defaultCheck: Boolean = true): Ui[Any] =\n    (this <~ vAddField(momentKey, moment)) ~\n      (icon <~\n        ivSrc(moment.getIconCollectionDetail)) ~\n      (name <~\n        tvText(moment.getName)) ~\n      (textConnected <~\n        tvText(R.string.wizard_new_conf_wifi_no_connected_step_3)) ~\n      (wifiAction <~ On.click(Ui(onWifiClick()))) ~\n      (if (defaultCheck) check() else uncheck())\n\n  def check(): Ui[Any] =\n    (this <~ vAddField(checkKey, true)) ~\n      (icon <~ vBackground(selectedDrawable)) ~\n      (name <~ tvColorResource(R.color.wizard_text_title)) ~\n      (textConnected <~ tvColorResource(R.color.wizard_text_message)) ~\n      (wifiAction <~ vClickable(true) <~ tivColor(resGetColor(R.color.wizard_text_title)))\n\n  def uncheck(): Ui[Any] =\n    (this <~ vAddField(checkKey, false)) ~\n      (icon <~ vBackground(unselectedDrawable)) ~\n      (name <~ tvColorResource(R.color.wizard_checkbox_unselected)) ~\n      (textConnected <~ tvColorResource(R.color.wizard_checkbox_unselected)) ~\n      (wifiAction <~ vClickable(false) <~ tivColor(\n        resGetColor(R.color.wizard_checkbox_unselected)))\n\n  def setWifiName(wifi: String): Ui[Any] =\n    (this <~ vAddField(wifiNameKey, wifi)) ~\n      (textConnected <~\n        tvText(resGetString(R.string.wizard_new_conf_wifi_connected_step_3, wifi)))\n\n  def swap(): Ui[Any] = this.getField[Boolean](checkKey) match {\n    case Some(true)  => uncheck()\n    case Some(false) => check()\n    case _           => Ui.nop\n  }\n\n  def isCheck: Boolean = this.getField[Boolean](checkKey) exists (c => c)\n\n  def getMoment: Option[NineCardsMoment] =\n    this.getField[NineCardsMoment](momentKey)\n\n  def getWifiName: Option[String] = this.getField[String](wifiNameKey)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/components/widgets/tweaks/WidgetsTweaks.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.components.widgets.tweaks\n\nimport android.graphics.Color\nimport android.view.animation.AnimationUtils\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.components.widgets._\nimport cards.nine.app.ui.launcher.types.{AppsMenuOption, ContactsMenuOption}\nimport cards.nine.models.{NineCardsTheme, PackagesByCategory}\nimport cards.nine.models.types.NineCardsMoment\nimport macroid._\n\nobject TintableImageViewTweaks {\n  type W = TintableImageView\n\n  def tivDefaultColor(color: Int)(implicit context: ContextWrapper): Tweak[W] =\n    Tweak[W] { view =>\n      view.defaultColor = color\n      view.setTint(color)\n    }\n\n  def tivPressedColor(color: Int)(implicit context: ContextWrapper): Tweak[W] =\n    Tweak[W](_.pressedColor = color)\n\n  def tivClean(implicit context: ContextWrapper): Tweak[W] = Tweak[W] { view =>\n    view.defaultColor = Color.WHITE\n    view.pressedColor = Color.WHITE\n    view.setTint(Color.WHITE)\n  }\n\n  def tivColor(color: Int)(implicit context: ContextWrapper): Tweak[W] =\n    Tweak[W] { view =>\n      view.defaultColor = color\n      view.pressedColor = color\n      view.setTint(color)\n    }\n\n}\n\nobject TintableButtonTweaks {\n  type W = TintableButton\n\n  def tbDefaultColor(color: Int)(implicit context: ContextWrapper): Tweak[W] =\n    Tweak[W] { view =>\n      view.defaultColor = color\n      view.setTint(color)\n    }\n\n  def tbPressedColor(color: Int)(implicit context: ContextWrapper): Tweak[W] =\n    Tweak[W](_.pressedColor = color)\n\n  def tbResetColor()(implicit context: ContextWrapper): Tweak[W] =\n    Tweak[W](_.setDefaultColor())\n\n}\n\nobject CollectionRecyclerViewTweaks {\n  type W = CollectionRecyclerView\n\n  def nrvDisableScroll(disable: Boolean) =\n    Tweak[W](view => view.statuses = view.statuses.copy(disableScroll = disable))\n\n  def nrvEnableAnimation(res: Int)(implicit contextWrapper: ContextWrapper) =\n    Tweak[W] { view =>\n      view.statuses = view.statuses.copy(enableAnimation = true)\n      view.setLayoutAnimation(AnimationUtils.loadLayoutAnimation(contextWrapper.application, res))\n    }\n\n  def nrvScheduleLayoutAnimation = Tweak[W](_.scheduleLayoutAnimation())\n\n}\n\nobject DrawerRecyclerViewTweaks {\n  type W = DrawerRecyclerView\n\n  def drvSetType(option: AppsMenuOption) = Tweak[W] { view =>\n    view.statuses = view.statuses.copy(contentView = AppsView)\n    (view <~ vSetType(option.name)).run\n  }\n\n  def drvSetType(option: ContactsMenuOption) = Tweak[W] { view =>\n    view.statuses = view.statuses.copy(contentView = ContactView)\n    (view <~ vSetType(option.name)).run\n  }\n\n}\n\nobject WizardCheckBoxTweaks {\n  type W = WizardCheckBox\n\n  def wcbInitialize(resText: Int, defaultCheck: Boolean = true) =\n    Tweak[W](_.initialize(resText, defaultCheck).run)\n\n  def wcbInitializeCollection(\n      packagesByCategory: PackagesByCategory,\n      defaultCheck: Boolean = true) =\n    Tweak[W](_.initializeCollection(packagesByCategory, defaultCheck).run)\n\n  def wcbDoCheck(doCheck: Boolean) = Tweak[W] { view =>\n    (if (doCheck) view.check() else view.uncheck()).run\n  }\n\n  def wcbCheck() = Tweak[W](_.check().run)\n\n  def wcbUncheck() = Tweak[W](_.uncheck().run)\n\n  def wcbSwap() = Tweak[W](_.swap().run)\n\n}\n\nobject WizardWifiCheckBoxTweaks {\n  type W = WizardWifiCheckBox\n\n  def wwcbInitialize(\n      moment: NineCardsMoment,\n      onWifiClick: () => Unit,\n      defaultCheck: Boolean = true) =\n    Tweak[W](_.initialize(moment, onWifiClick, defaultCheck).run)\n\n  def wwcbDoCheck(doCheck: Boolean) = Tweak[W] { view =>\n    (if (doCheck) view.check() else view.uncheck()).run\n  }\n\n  def wwcbCheck() = Tweak[W](_.check().run)\n\n  def wwcbUncheck() = Tweak[W](_.uncheck().run)\n\n  def wwcbSwap() = Tweak[W](_.swap().run)\n\n  def wwcbWifiName(wifi: String) = Tweak[W](_.setWifiName(wifi).run)\n}\n\nobject WizardMomentCheckBoxTweaks {\n  type W = WizardMomentCheckBox\n\n  def wmcbInitialize(moment: NineCardsMoment, defaultCheck: Boolean = true) =\n    Tweak[W](_.initialize(moment, defaultCheck).run)\n\n  def wmcbDoCheck(doCheck: Boolean) = Tweak[W] { view =>\n    (if (doCheck) view.check() else view.uncheck()).run\n  }\n\n  def wmcbCheck() = Tweak[W](_.check().run)\n\n  def wmcbUncheck() = Tweak[W](_.uncheck().run)\n\n  def wmcbSwap() = Tweak[W](_.swap().run)\n\n}\n\nobject CollectionCheckBoxTweaks {\n  type W = CollectionCheckBox\n\n  def ccbInitialize(\n      collectionIcon: Int,\n      color: Int,\n      theme: NineCardsTheme,\n      defaultCheck: Boolean = true) =\n    Tweak[W](_.initialize(collectionIcon, color, theme, defaultCheck).run)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/LauncherActivity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher\n\nimport android.app.Activity\nimport android.appwidget.{AppWidgetHost, AppWidgetManager}\nimport android.content.Intent\nimport android.os.Bundle\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.support.v7.app.AppCompatActivity\nimport android.view.KeyEvent\nimport cards.nine.app.commons.{BroadcastDispatcher, ContextSupportProvider}\nimport scala.concurrent.duration._\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.commons.action_filters._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.exceptions.{\n  ChangeMomentException,\n  LoadDataException,\n  SpaceException\n}\nimport cards.nine.app.ui.launcher.jobs._\nimport cards.nine.app.ui.launcher.jobs.uiactions._\nimport cards.nine.app.ui.launcher.types.AppsAlphabetical\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{CardData, Collection, NineCardsTheme, Widget}\nimport com.fortysevendeg.ninecardslauncher.{R, TypedFindView}\nimport macroid._\n\nclass LauncherActivity\n    extends AppCompatActivity\n    with Contexts[AppCompatActivity]\n    with ContextSupportProvider\n    with TypedFindView\n    with BroadcastDispatcher { self =>\n\n  implicit lazy val uiContext: UiContext[Activity] = ActivityUiContext(self)\n\n  lazy val launcherJobs = createLauncherJobs\n\n  lazy val appDrawerJobs = createAppDrawerJobs\n\n  lazy val navigationJobs = createNavigationJobs\n\n  lazy val dragJobs = createDragJobs\n\n  lazy val widgetJobs = createWidgetsJobs\n\n  override val actionsFilters: Seq[String] =\n    (MomentsActionFilter.cases map (_.action)) ++ (AppsActionFilter.cases map (_.action)) ++ (CollectionsActionFilter.cases map (_.action))\n\n  override def manageCommand(action: String, data: Option[String]): Unit = {\n    (MomentsActionFilter(action), AppsActionFilter(action), CollectionsActionFilter(action), data) match {\n      case (Some(MomentReloadedActionFilter), _, _, _) =>\n        launcherJobs.reloadAppsMomentBar().resolveAsync()\n      case (Some(MomentConstrainsChangedActionFilter), _, _, _) =>\n        launcherJobs.changeMomentIfIsAvailable(force = false, data).resolveAsync()\n      case (Some(MomentAddedOrRemovedActionFilter), _, _, _) =>\n        launcherJobs.reloadFence().resolveAsync()\n      case (Some(MomentBestAvailableActionFilter), _, _, _) =>\n        launcherJobs.changeMomentIfIsAvailable(force = false, data).resolveAsync()\n      case (Some(MomentForceBestAvailableActionFilter), _, _, _) =>\n        launcherJobs.changeMomentIfIsAvailable(force = true).resolveAsync()\n      case (_, Some(AppInstalledActionFilter), _, _) =>\n        appDrawerJobs.loadApps(AppsAlphabetical).resolveAsync()\n      case (_, Some(AppUninstalledActionFilter), _, _) =>\n        appDrawerJobs.loadApps(AppsAlphabetical).resolveAsync()\n      case (_, Some(AppUpdatedActionFilter), _, _) =>\n        appDrawerJobs.loadApps(AppsAlphabetical).resolveAsync()\n      case (_, _, Some(CollectionAddedActionFilter), Some(id)) =>\n        launcherJobs\n          .reloadCollection(id.toInt)\n          .resolveAsyncServiceOr(_ => launcherJobs.navigationUiActions.showContactUsError())\n      case _ =>\n    }\n  }\n\n  override def onCreate(bundle: Bundle): Unit = {\n    super.onCreate(bundle)\n    statuses = statuses.copy(\n      appWidgetManager = Option(AppWidgetManager.getInstance(this)),\n      appWidgetHost = Option(new AppWidgetHost(this, R.id.app_widget_host_id)))\n\n    setContentView(R.layout.launcher_activity)\n    registerDispatchers()\n    launcherJobs.initialize().resolveAsync()\n  }\n\n  override def onStart(): Unit = {\n    super.onStart()\n    launcherJobs.registerFence().resolveAsync()\n  }\n\n  override def onResume(): Unit = {\n    super.onResume()\n    launcherJobs\n      .resume()\n      .resolveAsync(\n        onResult = (_) =>\n          launcherJobs.workspaceUiActions\n            .openLauncherWizardInline()\n            .resolveAsyncDelayed(3.seconds),\n        onException = (ex) =>\n          ex match {\n            case _: LoadDataException =>\n              navigationJobs.navigationUiActions.goToWizard().resolveAsync()\n            case _: ChangeMomentException =>\n              launcherJobs.reloadAppsMomentBar().resolveAsync()\n            case _: UiException =>\n              launcherJobs.loadLauncherInfo().resolveAsync()\n            case _ =>\n        }\n      )\n  }\n\n  override def onPause(): Unit = {\n    super.onPause()\n    launcherJobs.pause().resolveAsync()\n  }\n\n  override def onStop(): Unit = {\n    super.onStop()\n    launcherJobs.unregisterFence().resolveAsync()\n  }\n\n  override def onDestroy(): Unit = {\n    super.onDestroy()\n    unregisterDispatcher()\n    launcherJobs.destroy().resolveAsync()\n  }\n\n  override def onBackPressed(): Unit = back().resolveAsync()\n\n  override def onWindowFocusChanged(hasFocus: Boolean): Unit = {\n    super.onWindowFocusChanged(hasFocus)\n    statuses = statuses.copy(hasFocus = hasFocus)\n  }\n\n  override def onNewIntent(intent: Intent): Unit = {\n    super.onNewIntent(intent)\n    val alreadyOnHome = statuses.hasFocus && ((intent.getFlags &\n        Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)\n        != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)\n    if (alreadyOnHome) {\n      back().resolveAsync()\n    } else if (launcherJobs.mainLauncherUiActions.dom.isDrawerVisible) {\n      appDrawerJobs.mainAppDrawerUiActions.close().resolveAsync()\n    }\n  }\n\n  override def dispatchKeyEvent(event: KeyEvent): Boolean =\n    (event.getAction, event.getKeyCode) match {\n      case (KeyEvent.ACTION_DOWN | KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HOME) =>\n        true\n      case _ => super.dispatchKeyEvent(event)\n    }\n\n  override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit = {\n\n    def getExtraAppWidgetId =\n      Option(data) flatMap (d => Option(d.getExtras)) flatMap { extras =>\n        val id = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, 0)\n        if (id == 0) None else Some(id)\n      }\n\n    (requestCode, resultCode) match {\n      case (RequestCodes.goToCollectionDetails, _) =>\n        launcherJobs.mainLauncherUiActions.resetFromCollection().resolveAsync()\n      case (RequestCodes.goToProfile, ResultCodes.logoutSuccessful) =>\n        (for {\n          _ <- launcherJobs.workspaceUiActions.cleanWorkspaces()\n          _ <- launcherJobs.navigationUiActions.goToWizard()\n        } yield ()).resolveAsync()\n      case (RequestCodes.goToWidgets, Activity.RESULT_OK) =>\n        widgetJobs.configureOrAddWidget(getExtraAppWidgetId).resolveAsync()\n      case (RequestCodes.goToConfigureWidgets, Activity.RESULT_OK) =>\n        widgetJobs.addWidget(getExtraAppWidgetId).resolveAsyncServiceOr[Throwable] {\n          case ex: SpaceException =>\n            widgetJobs.navigationUiActions.showWidgetNoHaveSpaceMessage()\n          case _ => widgetJobs.navigationUiActions.showContactUsError()\n        }\n      case (\n          RequestCodes.goToConfigureWidgets | RequestCodes.goToWidgets,\n          Activity.RESULT_CANCELED) =>\n        widgetJobs.cancelWidget(getExtraAppWidgetId).resolveAsync()\n      case (RequestCodes.goToPreferences, ResultCodes.preferencesChanged) =>\n        launcherJobs\n          .preferencesChanged(data.getStringArrayExtra(ResultData.preferencesResultData))\n          .resolveAsync()\n      case _ =>\n    }\n  }\n\n  override def onRequestPermissionsResult(\n      requestCode: Int,\n      permissions: Array[String],\n      grantResults: Array[Int]): Unit =\n    launcherJobs\n      .requestPermissionsResult(requestCode, permissions, grantResults)\n      .resolveAsyncServiceOr { _ =>\n        launcherJobs.navigationUiActions.showContactUsError()\n      }\n\n  private[this] def back() =\n    if (statuses.mode == EditWidgetsMode) {\n      statuses.transformation match {\n        case Some(_) => widgetJobs.backToActionEditWidgets()\n        case _       => widgetJobs.closeModeEditWidgets()\n      }\n    } else if (launcherJobs.mainLauncherUiActions.dom.isDrawerTabsOpened) {\n      appDrawerJobs.mainAppDrawerUiActions.closeTabs()\n    } else if (launcherJobs.mainLauncherUiActions.dom.isMenuVisible) {\n      launcherJobs.menuDrawersUiActions.close()\n    } else if (launcherJobs.mainLauncherUiActions.dom.isAppsByMomentMenuVisible) {\n      launcherJobs.menuDrawersUiActions.closeAppsMoment()\n    } else if (launcherJobs.mainLauncherUiActions.dom.isDrawerVisible) {\n      appDrawerJobs.mainAppDrawerUiActions.close()\n    } else if (launcherJobs.mainLauncherUiActions.dom.isActionShowed) {\n      launcherJobs.navigationUiActions.unrevealActionFragment\n    } else if (launcherJobs.mainLauncherUiActions.dom.isBackgroundMenuVisible) {\n      launcherJobs.workspaceUiActions.closeBackgroundMenu()\n    } else {\n      TaskService.empty\n    }\n}\n\nobject LauncherActivity {\n\n  var statuses = LauncherStatuses()\n\n  def createLauncherJobs(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]) = {\n    val dom = new LauncherDOM(activityContextWrapper.getOriginal)\n    new LauncherJobs(\n      mainLauncherUiActions = new LauncherUiActions(dom),\n      workspaceUiActions = new WorkspaceUiActions(dom),\n      menuDrawersUiActions = new MenuDrawersUiActions(dom),\n      appDrawerUiActions = new AppDrawerUiActions(dom),\n      navigationUiActions = new NavigationUiActions(dom),\n      dockAppsUiActions = new DockAppsUiActions(dom),\n      topBarUiActions = new TopBarUiActions(dom),\n      widgetUiActions = new WidgetUiActions(dom),\n      dragUiActions = new DragUiActions(dom))\n  }\n\n  def createAppDrawerJobs(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]) = {\n    val dom = new LauncherDOM(activityContextWrapper.getOriginal)\n    new AppDrawerJobs(new AppDrawerUiActions(dom))\n  }\n\n  def createNavigationJobs(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]) = {\n    val dom = new LauncherDOM(activityContextWrapper.getOriginal)\n    new NavigationJobs(\n      navigationUiActions = new NavigationUiActions(dom),\n      menuDrawersUiActions = new MenuDrawersUiActions(dom),\n      widgetUiActions = new WidgetUiActions(dom),\n      appDrawerUiActions = new AppDrawerUiActions(dom))\n  }\n\n  def createWidgetsJobs(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]) = {\n    val dom = new LauncherDOM(activityContextWrapper.getOriginal)\n    new WidgetsJobs(new WidgetUiActions(dom), new NavigationUiActions(dom))\n  }\n\n  def createDragJobs(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]) = {\n    val dom = new LauncherDOM(activityContextWrapper.getOriginal)\n    new DragJobs(\n      mainAppDrawerUiActions = new AppDrawerUiActions(dom),\n      dragUiActions = new DragUiActions(dom),\n      navigationUiActions = new NavigationUiActions(dom),\n      dockAppsUiActions = new DockAppsUiActions(dom),\n      workspaceUiActions = new WorkspaceUiActions(dom))\n  }\n\n}\n\ncase class LauncherStatuses(\n    appWidgetManager: Option[AppWidgetManager] = None,\n    appWidgetHost: Option[AppWidgetHost] = None,\n    theme: NineCardsTheme = AppUtils.getDefaultTheme,\n    touchingWidget: Boolean = false, // This parameter is for controlling scrollable widgets\n    hasFocus: Boolean = false,\n    hostingNoConfiguredWidget: Option[Widget] = None,\n    mode: LauncherMode = NormalMode,\n    transformation: Option[EditWidgetTransformation] = None,\n    idWidget: Option[Int] = None,\n    cardAddItemMode: Option[CardData] = None,\n    collectionReorderMode: Option[Collection] = None,\n    startPositionReorderMode: Int = 0,\n    currentDraggingPosition: Int = 0,\n    lastPhone: Option[String] = None) {\n\n  def startAddItem(card: CardData): LauncherStatuses =\n    copy(mode = AddItemMode, cardAddItemMode = Some(card))\n\n  def startReorder(collection: Collection, position: Int): LauncherStatuses =\n    copy(\n      startPositionReorderMode = position,\n      collectionReorderMode = Some(collection),\n      currentDraggingPosition = position,\n      mode = ReorderMode)\n\n  def updateCurrentPosition(position: Int): LauncherStatuses =\n    copy(currentDraggingPosition = position)\n\n  def reset(): LauncherStatuses =\n    copy(\n      startPositionReorderMode = 0,\n      cardAddItemMode = None,\n      collectionReorderMode = None,\n      currentDraggingPosition = 0,\n      mode = NormalMode)\n\n  def isReordering: Boolean = mode == ReorderMode\n\n}\n\nsealed trait LauncherMode\n\ncase object NormalMode extends LauncherMode\n\ncase object AddItemMode extends LauncherMode\n\ncase object ReorderMode extends LauncherMode\n\ncase object EditWidgetsMode extends LauncherMode\n\nsealed trait EditWidgetTransformation\n\ncase object ResizeTransformation extends EditWidgetTransformation\n\ncase object MoveTransformation extends EditWidgetTransformation\n\nsealed trait DragArea\n\ncase object ActionsDragArea extends DragArea\n\ncase object WorkspacesDragArea extends DragArea\n\ncase object DockAppsDragArea extends DragArea\n\ncase object NoDragArea extends DragArea\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/exceptions/LauncherExceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.exceptions\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class SpaceException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsSpaceException {\n  implicit def momentException =\n    (t: Throwable) => SpaceException(t.getMessage, Option(t))\n}\n\ncase class LoadDataException(\n    message: String,\n    cause: Option[Throwable] = None,\n    recoverable: Boolean = false)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsLoadDataException {\n  implicit def loadDataExceptionConverter =\n    (t: Throwable) => LoadDataException(t.getMessage, Option(t))\n}\n\ncase class ChangeMomentException(\n    message: String,\n    cause: Option[Throwable] = None,\n    recoverable: Boolean = false)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsChangeMomentException {\n  implicit def changeMomentExceptionConverter =\n    (t: Throwable) => ChangeMomentException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/holders/LauncherWorkSpaceCollectionsHolder.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.holders\n\nimport android.content.{ClipData, Context}\nimport android.graphics.Point\nimport android.os.Handler\nimport android.view.DragEvent._\nimport android.view.{LayoutInflater, View}\nimport android.widget._\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.Constants._\nimport macroid.extras.UIActionsExtras._\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons.ops.CollectionOps._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.drawables.DropBackgroundDrawable\nimport cards.nine.app.ui.components.layouts.tweaks.LauncherWorkSpacesTweaks._\nimport cards.nine.app.ui.components.layouts.{Dimen, LauncherWorkSpaceHolder, LauncherWorkSpaces}\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.holders.LauncherWorkSpaceCollectionsHolder.positionDraggingItem\nimport cards.nine.app.ui.launcher.jobs.{DragJobs, NavigationJobs}\nimport cards.nine.app.ui.launcher.types.{CollectionShadowBuilder, DragObject, ReorderCollection}\nimport cards.nine.app.ui.preferences.commons.{FontSize, IconsSize, SpeedAnimations}\nimport cards.nine.commons.ops.SeqOps._\nimport cards.nine.models.Collection\nimport cards.nine.models.NineCardsTheme\nimport macroid.extras.GridLayoutTweaks._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.TypedResource._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\nclass LauncherWorkSpaceCollectionsHolder(context: Context, parentDimen: Dimen)(\n    implicit dragJobs: DragJobs,\n    navigationJobs: NavigationJobs,\n    theme: NineCardsTheme)\n    extends LauncherWorkSpaceHolder(context)\n    with Contexts[View]\n    with TypedFindView {\n\n  LayoutInflater.from(context).inflate(R.layout.collections_workspace_layout, this)\n\n  val selectedScale = 1.1f\n\n  val defaultScale = 1f\n\n  val handler = new Handler()\n\n  var task: Option[Runnable] = None\n\n  var moveTask: Option[Runnable] = None\n\n  val sizeEdgeBetweenWorkspaces = resGetDimensionPixelSize(R.dimen.size_edge_between_workspaces)\n\n  val widthSpace = parentDimen.width / numInLine\n\n  val heightSpace = parentDimen.height / numInLine\n\n  var positionScreen = 0\n\n  val grid = Option(findView(TR.launcher_collections_grid))\n\n  val views: Seq[CollectionItem] = 0 until numSpaces map (_ => new CollectionItem(context))\n\n  (grid <~\n    glAddViews(\n      views = views,\n      columns = numInLine,\n      rows = numInLine,\n      width = widthSpace,\n      height = heightSpace)).run\n\n  def populate(collections: Seq[Collection], positionScreen: Int): Ui[_] = {\n    this.positionScreen = positionScreen\n    val uiSeq = for {\n      row    <- 0 until numInLine\n      column <- 0 until numInLine\n    } yield {\n      val position = (row * numInLine) + column\n      val view = grid flatMap (_.getChildAt(position) match {\n        case item: CollectionItem => Some(item)\n        case _                    => None\n      })\n      collections.lift(position) map { collection =>\n        view <~ ciPopulate(collection)\n      } getOrElse view <~ ciOff()\n    }\n    Ui.sequence(uiSeq: _*)\n  }\n\n  def prepareItemsScreenInReorder(position: Int): Ui[Any] = {\n    val startReorder = statuses.startPositionReorderMode\n    val screenOfCollection = (toPositionCollection(0) <= startReorder) && (toPositionCollection(\n        numSpaces) > startReorder)\n    def isConvertToDragging(pos: Int) =\n      if (screenOfCollection) {\n        pos == startReorder\n      } else {\n        pos == toPositionCollection(position)\n      }\n    Ui.sequence(views map { view =>\n      if (isConvertToDragging(view.positionInGrid)) {\n        view.convertToDraggingItem() ~ (view <~ vInvisible)\n      } else if (view.collection.isEmpty) {\n        view <~ vInvisible\n      } else {\n        view <~ vVisible\n      }\n    }: _*) ~ reorder(startReorder, position, animation = false)\n  }\n\n  def dragAddItemController(action: Int, x: Float, y: Float): Unit = {\n    action match {\n      case ACTION_DRAG_LOCATION =>\n        val lastCurrentPosition             = statuses.currentDraggingPosition\n        val (canMoveToLeft, canMoveToRight) = canMove\n        (calculateEdge(x), canMoveToLeft, canMoveToRight) match {\n          case (LeftEdge, true, _) =>\n            unselectAll().run\n            delayedTask(() => {\n              dragJobs\n                .draggingAddItemToPreviousScreen(toPositionCollection(numSpaces - 1) - numSpaces)\n                .resolveAsync()\n            })\n          case (RightEdge, _, true) =>\n            unselectAll().run\n            delayedTask(() => {\n              dragJobs\n                .draggingAddItemToNextScreen(toPositionCollection(0) + numSpaces)\n                .resolveAsync()\n            })\n          case (NoEdge, _, _) =>\n            clearTask()\n            val space           = calculatePosition(x, y)\n            val currentPosition = toPositionCollection(space)\n            if (lastCurrentPosition != currentPosition) {\n              select(currentPosition).run\n              dragJobs.draggingAddItemTo(currentPosition).resolveAsync()\n            }\n          case _ =>\n        }\n      case ACTION_DROP =>\n        unselectAll().run\n        dragJobs\n          .endAddItemToCollection()\n          .resolveAsyncServiceOr(_ => dragJobs.navigationUiActions.showContactUsError())\n      case ACTION_DRAG_EXITED =>\n        unselectAll().run\n      case ACTION_DRAG_ENDED =>\n        unselectAll().run\n        dragJobs.endAddItem().resolveAsync()\n      case _ =>\n    }\n  }\n\n  def dragReorderCollectionController(action: Int, x: Float, y: Float): Unit = {\n    (action, statuses.isReordering, isRunningReorderAnimation) match {\n      case (ACTION_DRAG_LOCATION, true, false) =>\n        val lastCurrentPosition             = statuses.currentDraggingPosition\n        val (canMoveToLeft, canMoveToRight) = canMove\n        (calculateEdge(x), canMoveToLeft, canMoveToRight) match {\n          case (LeftEdge, true, _) =>\n            clearMoveTask()\n            delayedTask(() => {\n              resetAllPositions().run\n              dragJobs\n                .draggingReorderToPreviousScreen(toPositionCollection(numSpaces - 1) - numSpaces)\n                .resolveAsync()\n            })\n          case (RightEdge, _, true) =>\n            clearMoveTask()\n            delayedTask(() => {\n              resetAllPositions().run\n              dragJobs\n                .draggingReorderToNextScreen(toPositionCollection(0) + numSpaces)\n                .resolveAsync()\n            })\n          case (NoEdge, _, _) =>\n            clearTask()\n            val space = calculatePosition(x, y)\n            val existCollectionInSpace =\n              (views.lift(space) flatMap (_.collection)).isDefined\n            val currentPosition = toPositionCollection(space)\n            if (existCollectionInSpace && lastCurrentPosition != currentPosition) {\n              delayedMoveTask(() => {\n                reorder(lastCurrentPosition, currentPosition).run\n                dragJobs.draggingReorderTo(currentPosition).resolveAsync()\n              })\n            }\n          case _ => clearTask()\n        }\n      case (ACTION_DROP | ACTION_DRAG_ENDED, true, false) =>\n        dragEnded()\n      case (ACTION_DROP | ACTION_DRAG_ENDED, true, true) =>\n        // we are waiting that the animation is finished in order to reset views\n        delayedTask(dragEnded, SpeedAnimations.getDuration)\n      case (ACTION_DRAG_EXITED, _, _) => clearMoveTask()\n      case _                          =>\n    }\n  }\n\n  private[this] def canMove: (Boolean, Boolean) = getParent.getParent match {\n    case workspaces: LauncherWorkSpaces =>\n      ((workspaces ~> lwsCanMoveToPreviousScreenOnlyCollections()).get,\n       (workspaces ~> lwsCanMoveToNextScreenOnlyCollections()).get)\n    case _ => (false, false)\n  }\n\n  private[this] def dragEnded(): Unit = {\n    clearTask()\n    resetPlaces.run\n    dragJobs.dropReorder().resolveAsyncServiceOr(_ => dragJobs.dropReorderException())\n  }\n\n  private[this] def resetAllPositions(): Ui[Any] =\n    Ui.sequence(views map { view =>\n      view <~ backToPosition() <~ (view.collection map (_ => vVisible) getOrElse vInvisible)\n    }: _*)\n\n  private[this] def select(position: Int): Ui[Any] =\n    Ui.sequence(views map { view =>\n      view <~ (if (view.positionInGrid == position) ciDroppingOn()\n               else ciDroppingOff())\n    }: _*)\n\n  private[this] def unselectAll(): Ui[Any] = select(Int.MaxValue)\n\n  private[this] def reorder(\n      currentPosition: Int,\n      toPosition: Int,\n      animation: Boolean = true): Ui[Any] =\n    if (currentPosition < toPosition) {\n      val from = currentPosition + 1\n      val to   = toPosition\n      val transforms = from to to map { pos =>\n        move(pos, pos - 1, animation)\n      }\n      val updatePositions = from to to map { pos =>\n        getView(pos) map (view => Ui(view.positionInGrid = pos - 1)) getOrElse Ui.nop\n      }\n      Ui.sequence(transforms ++ updatePositions: _*)\n    } else if (currentPosition > toPosition) {\n      val from = toPosition\n      val to   = currentPosition\n      val transforms = from until to map { pos =>\n        move(pos, pos + 1, animation)\n      }\n      val updatePositions = from until to map { pos =>\n        getView(pos) map (view => Ui(view.positionInGrid = pos + 1)) getOrElse Ui.nop\n      }\n      Ui.sequence(transforms ++ updatePositions: _*)\n    } else {\n      Ui.nop\n    }\n\n  private[this] def move(from: Int, to: Int, animation: Boolean): Ui[Any] = {\n    val (fromColumn, fromRow)  = place(from)\n    val (toColumn, toRow)      = place(to)\n    val displacementHorizontal = (toColumn - fromColumn) * widthSpace\n    val displacementVertical   = (toRow - fromRow) * heightSpace\n    val view                   = getView(from)\n    if (animation) {\n      view <~ applyAnimation(xBy = Some(displacementHorizontal), yBy = Some(displacementVertical))\n    } else {\n      view <~\n        vTranslationX(displacementHorizontal) <~\n        vTranslationY(displacementVertical)\n    }\n  }\n\n  private[this] def resetPlaces: Ui[Any] = {\n    val start   = toPositionGrid(statuses.startPositionReorderMode)\n    val current = toPositionGrid(statuses.currentDraggingPosition)\n    val collectionsReordered = (views map (_.collection))\n        .reorder(start, current)\n        .zipWithIndex map {\n      case (collection, index) =>\n        val positionCollection = toPositionCollection(index)\n        // if it's the collection that the user is dragging, we put the collection stored.\n        // when the user is reordering in other screen the collection isn't the same on the view\n        if (positionCollection == statuses.currentDraggingPosition) {\n          statuses.collectionReorderMode\n        } else {\n          collection map (_.copy(position = positionCollection))\n        }\n    }\n    Ui.sequence(views.zip(collectionsReordered) map {\n      case (view, Some(collection)) =>\n        view <~\n          vClearAnimation <~\n          backToPosition() <~\n          ciPopulate(collection)\n      case _ => Ui.nop\n    }: _*)\n  }\n\n  private[this] def place(pos: Int): (Int, Int) = {\n    val row    = pos / numInLine\n    val column = pos % numInLine\n    (column, row)\n  }\n\n  private[this] def getView(position: Int): Option[CollectionItem] =\n    views.find(_.positionInGrid == position)\n\n  private[this] def calculatePosition(x: Float, y: Float): Int = {\n    val column = x.toInt / widthSpace\n    val row    = y.toInt / heightSpace\n    (row * numInLine) + column\n  }\n\n  private[this] def calculateEdge(x: Float): Edge =\n    if (x < sizeEdgeBetweenWorkspaces) {\n      LeftEdge\n    } else if (x > parentDimen.width - sizeEdgeBetweenWorkspaces) {\n      RightEdge\n    } else {\n      NoEdge\n    }\n\n  private[this] def toPositionCollection(position: Int) =\n    position + (positionScreen * numSpaces)\n\n  private[this] def toPositionGrid(position: Int) =\n    position - (positionScreen * numSpaces)\n\n  private[this] def isRunningReorderAnimation: Boolean =\n    views exists (_.isRunningAnimation)\n\n  private[this] def delayedMoveTask(runTask: () => Unit, duration: Int = 200): Unit = {\n    moveTask foreach handler.removeCallbacks\n    val runnable = new Runnable {\n      override def run(): Unit = runTask()\n    }\n    moveTask = Option(runnable)\n    handler.postDelayed(runnable, duration)\n  }\n\n  private[this] def clearMoveTask(): Unit = if (moveTask.isDefined) {\n    moveTask foreach handler.removeCallbacks\n    moveTask = None\n  }\n\n  private[this] def delayedTask(runTask: () => Unit, duration: Int = 500): Unit =\n    if (task.isEmpty) {\n      val runnable = new Runnable {\n        override def run(): Unit = runTask()\n      }\n      task = Option(runnable)\n      handler.postDelayed(runnable, duration)\n    }\n\n  private[this] def clearTask(): Unit = if (task.isDefined) {\n    task foreach handler.removeCallbacks\n    task = None\n  }\n\n  private[this] def backToPosition() = vTranslationX(0) + vTranslationY(0)\n\n  private[this] def ciPopulate(collection: Collection) =\n    vVisible + Tweak[CollectionItem](_.populate(collection))\n\n  private[this] def ciOff() =\n    vInvisible + Tweak[CollectionItem](_.collection = None)\n\n  private[this] def ciDroppingOn() = Tweak[CollectionItem](_.droppingOn().run)\n\n  private[this] def ciDroppingOff() =\n    Tweak[CollectionItem](_.droppingOff().run)\n\n  class CollectionItem(context: Context) extends FrameLayout(context) with TypedFindView { self =>\n\n    LayoutInflater.from(getContext).inflate(TR.layout.collection_item, self, true)\n\n    var positionInGrid = 0\n\n    var collection: Option[Collection] = None\n\n    val displacement = resGetDimensionPixelSize(R.dimen.shadow_displacement_default)\n\n    val radius = resGetDimensionPixelSize(R.dimen.shadow_radius_default)\n\n    lazy val layout = findView(TR.launcher_collection_item_layout)\n\n    lazy val iconRoot = findView(TR.launcher_collection_item_icon_root)\n\n    lazy val icon = findView(TR.launcher_collection_item_icon)\n\n    lazy val name = findView(TR.launcher_collection_item_name)\n\n    val dropBackgroundIcon = new DropBackgroundDrawable\n\n    ((layout <~ vUseLayerHardware) ~\n      (name <~ tvShadowLayer(\n        radius,\n        displacement,\n        displacement,\n        resGetColor(R.color.shadow_default)))).run\n\n    def populate(collection: Collection): Unit = {\n      this.collection = Some(collection)\n      positionInGrid = collection.position\n      val resIcon = collection.getIconWorkspace\n      ((layout <~\n        FuncOn.click { view: View =>\n          val (x, y) = view.calculateAnchorViewPosition\n          val point =\n            new Point(x + (view.getWidth / 2), y + (view.getHeight / 2))\n          Ui(navigationJobs.goToCollection(this.collection, point).resolveAsync())\n        } <~\n        On.longClick {\n          dragJobs.startReorder(this.collection, positionInGrid).resolveAsync()\n          (this.collection map { _ =>\n            (this <~ vInvisible) ~\n              convertToDraggingItem() ~\n              (layout <~ vStartDrag(\n                ReorderCollection,\n                new CollectionShadowBuilder(layout),\n                Option(collection.id.toString),\n                Option(collection.name)))\n          } getOrElse Ui.nop) ~ Ui(true)\n        }) ~\n        (icon <~ vResize(IconsSize.getIconCollection) <~ ivSrc(resIcon) <~ vBackgroundCollection(\n          collection.themedColorIndex)) ~\n        (name <~ tvSizeResource(FontSize.getSizeResource) <~ tvText(collection.name))).run\n    }\n\n    def convertToDraggingItem(): Ui[Any] =\n      Ui(positionInGrid = positionDraggingItem)\n\n    def droppingOn(): Ui[Any] =\n      (iconRoot <~ vBackground(dropBackgroundIcon)) ~\n        dropBackgroundIcon.start() ~\n        (name <~ vInvisible)\n\n    def droppingOff(): Ui[Any] =\n      dropBackgroundIcon.end() ~~\n        (iconRoot <~ vBlankBackground) ~\n        (name <~ vVisible)\n\n  }\n\n}\n\nobject LauncherWorkSpaceCollectionsHolder {\n  val positionDraggingItem = Int.MaxValue\n}\n\nsealed trait Edge\n\ncase object LeftEdge extends Edge\n\ncase object RightEdge extends Edge\n\ncase object NoEdge extends Edge\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/holders/LauncherWorkSpaceMomentsHolder.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.holders\n\nimport android.appwidget.AppWidgetHostView\nimport android.content.Context\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.RoundRectShape\nimport android.view.DragEvent._\nimport android.view.View\nimport android.view.ViewGroup.LayoutParams._\nimport android.widget.FrameLayout.LayoutParams\nimport android.widget.ImageView\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.ops.WidgetsOps._\nimport cards.nine.app.ui.commons.ops.WidgetsOps\nimport cards.nine.app.ui.commons.ops.WidgetsOps.Cell\nimport cards.nine.app.ui.components.drawables.DottedDrawable\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.layouts.{Dimen, LauncherWorkSpaceHolder}\nimport cards.nine.app.ui.components.models.LauncherMoment\nimport cards.nine.app.ui.components.widgets.LauncherWidgetView._\nimport cards.nine.app.ui.components.widgets.{\n  LauncherNoConfiguredWidgetView,\n  LauncherWidgetResizeFrame,\n  LauncherWidgetView\n}\nimport cards.nine.app.ui.commons.ops.ViewGroupOps._\nimport cards.nine.commons._\nimport cards.nine.models.{NineCardsTheme, Widget, WidgetArea}\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.jobs.WidgetsJobs\n\nclass LauncherWorkSpaceMomentsHolder(context: Context, parentDimen: Dimen)(\n    implicit widgetJobs: WidgetsJobs,\n    theme: NineCardsTheme)\n    extends LauncherWorkSpaceHolder(context)\n    with Contexts[View]\n    with TypedFindView {\n\n  val ruleTag = \"rule\"\n\n  case class WidgetStatuses(lastX: Option[Int] = None, lastY: Option[Int] = None) {\n    def reset(): WidgetStatuses = copy(None, None)\n  }\n\n  var widgetStatuses = WidgetStatuses()\n\n  lazy val onResizeChangeArea: (WidgetArea) => Boolean = (area) => {\n\n    val (current, others) = partitionWidgets()\n\n    current.headOption match {\n      case Some(_) =>\n        val hasSpaceAfterMovement: Boolean = (others map { w =>\n          !w.widgetStatuses.widget.area.intersect(area)\n        }).foldLeft(true) { (hasSpace, elem) =>\n          if (!hasSpace) false else elem\n        }\n        resizeWidget(area).ifUi(hasSpaceAfterMovement).run\n        hasSpaceAfterMovement\n      case _ => false\n    }\n  }\n\n  lazy val onResizeFinished: () => Unit = () => {\n    widgetJobs.closeModeEditWidgets().resolveAsync()\n  }\n\n  val radius = resGetDimensionPixelSize(R.dimen.radius_default)\n\n  val paddingDefault = resGetDimensionPixelSize(R.dimen.padding_default)\n\n  val stroke = resGetDimensionPixelSize(R.dimen.stroke_thin)\n\n  val (widthCell: Int, heightCell: Int) = {\n    val widthW  = parentDimen.width - (paddingDefault * 2)\n    val heightW = parentDimen.height - (paddingDefault * 2)\n    (widthW / columns, heightW / rows)\n  }\n\n  val drawable = {\n    val s = 0 until 8 map (_ => radius.toFloat)\n    val d = new ShapeDrawable(new RoundRectShape(s.toArray, javaNull, javaNull))\n    d.getPaint.setColor(resGetColor(R.color.moment_workspace_background))\n    d\n  }\n\n  def getWidgets: Seq[Widget] = this.children.collect {\n    case lwv: LauncherWidgetView => lwv.widgetStatuses.widget\n  }\n\n  def populate(moment: LauncherMoment): Ui[Any] =\n    moment.momentType map (moment =>\n                             Ui {\n                               widgetJobs\n                                 .loadWidgetsForMoment(moment)\n                                 .resolveAsyncServiceOr(_ =>\n                                   widgetJobs.navigationUiActions.showContactUsError())\n                             }) getOrElse clearWidgets\n\n  def dragReorderController(action: Int, x: Float, y: Float): Unit = {\n    val (sx, sy) = calculatePosition(x, y)\n    ((action, sx, sy, widgetStatuses.lastX, widgetStatuses.lastY) match {\n      case (ACTION_DRAG_LOCATION, currentX, currentY, lastX, lastY)\n          if lastX.isEmpty || lastY.isEmpty || !lastX.contains(currentX) || !lastY.contains(\n            currentY) =>\n        widgetStatuses = widgetStatuses.copy(lastX = Option(currentX), lastY = Option(currentY))\n        relocatedWidget(currentX, currentY)\n      case (ACTION_DROP | ACTION_DRAG_ENDED, _, _, _, _) =>\n        widgetStatuses = widgetStatuses.reset()\n        activeResizeWidget()\n      case _ => Ui.nop\n    }).run\n  }\n\n  def relocatedWidget(newCellX: Int, newCellY: Int): Ui[Any] = {\n    val (current, others) = partitionWidgets()\n\n    current.headOption match {\n      case Some(currentWidget) =>\n        val newWidget =\n          currentWidget.widgetStatuses.widget.convert(newCellX, newCellY)\n        val newMovement =\n          currentWidget.widgetStatuses.widget.getMovement(newWidget.area)\n\n        val hasSpaceAfterMovement: Boolean = (others map { w =>\n          w.widgetStatuses.widget.hasSpaceAfterMovement(\n            newWidget.area,\n            others.filterNot(_ == w) map (_.widgetStatuses.widget.area),\n            newMovement)\n        }).foldLeft(true) { (hasSpace, elem) =>\n          if (!hasSpace) false else elem\n        }\n\n        if (hasSpaceAfterMovement) {\n          val updateOtherWidgets = others flatMap { w =>\n            val betterPlace = w.widgetStatuses.widget.moveToBetterPlace(\n              newWidget.area,\n              others.filterNot(_ == w) map (_.widgetStatuses.widget.area),\n              newMovement)\n            betterPlace map w.updateView\n          }\n          Ui.sequence(updateOtherWidgets: _*) ~\n            currentWidget.updateView(newWidget) ~\n            reloadResizeFrame(newWidget.area)\n        } else {\n          Ui.nop\n        }\n      case _ => Ui.nop\n    }\n  }\n\n  def activeResizeWidget(): Ui[Any] = this <~ Transformer {\n    case widgetView: LauncherWidgetView\n        if statuses.idWidget.contains(widgetView.widgetStatuses.widget.id) =>\n      widgetView.activeResize()\n    case frame: LauncherWidgetResizeFrame => frame.activeResize()\n  }\n\n  def resizeWidget(area: WidgetArea): Ui[Any] = this <~ Transformer {\n    case widgetView: LauncherWidgetView\n        if statuses.idWidget.contains(widgetView.widgetStatuses.widget.id) =>\n      widgetView.updateView(widgetView.widgetStatuses.widget.copy(area = area))\n  }\n\n  def startEditWidget(): Ui[Any] =\n    (this <~ Transformer {\n      case widgetView: LauncherWidgetView\n          if statuses.idWidget.contains(widgetView.widgetStatuses.widget.id) =>\n        val frame =\n          new LauncherWidgetResizeFrame(\n            widgetView.widgetStatuses.widget.area,\n            widthCell,\n            heightCell,\n            onResizeChangeArea,\n            onResizeFinished)\n        widgetView.activeSelected() ~ (this <~ vgAddView(frame)) ~ frame.updateView(\n          widgetView.widgetStatuses.widget.area)\n      case widgetView: LauncherWidgetView => widgetView.deactivateSelected()\n    }) ~ createRules\n\n  def closeEditWidget(): Ui[Any] =\n    (this <~ Transformer {\n      case widgetView: LauncherWidgetView\n          if statuses.idWidget.contains(widgetView.widgetStatuses.widget.id) =>\n        widgetView.activeSelected()\n      case widgetView: LauncherWidgetView => widgetView.deactivateSelected()\n    }) ~ removeRulesAnResizeFrame()\n\n  def reloadSelectedWidget: Ui[Any] = this <~ Transformer {\n    case widgetView: LauncherWidgetView\n        if statuses.idWidget.contains(widgetView.widgetStatuses.widget.id) =>\n      widgetView.activeSelected()\n    case widgetView: LauncherWidgetView => widgetView.deactivateSelected()\n  }\n\n  def reloadResizeFrame(area: WidgetArea): Ui[Any] = this <~ Transformer {\n    case frame: LauncherWidgetResizeFrame => frame.updateView(area)\n  }\n\n  def addWidget(widgetView: AppWidgetHostView, cell: Cell, widget: Widget): Ui[Any] = {\n    val launcherWidgetView =\n      (new LauncherWidgetView(widget, widgetView) <~ saveInfoInTag(cell, widget)).get\n    this <~ launcherWidgetView.addView(cell, widget)\n  }\n\n  def addNoConfiguredWidget(wCell: Int, hCell: Int, widget: Widget): Ui[Any] = {\n    val noConfiguredWidgetView =\n      LauncherNoConfiguredWidgetView(widget.id, wCell, hCell, widget)\n    this <~ noConfiguredWidgetView.addView()\n  }\n\n  def addReplaceWidget(\n      widgetView: AppWidgetHostView,\n      wCell: Int,\n      hCell: Int,\n      widget: Widget): Ui[Any] = {\n    val cell = Cell(widget.area.spanX, widget.area.spanY, wCell, hCell)\n    (this <~ Transformer {\n      case i: LauncherNoConfiguredWidgetView if i.id == widget.id =>\n        this <~ vgRemoveView(i)\n    }) ~ addWidget(widgetView, cell, widget)\n  }\n\n  def clearWidgets: Ui[Any] = this <~ vgRemoveAllViews\n\n  def unhostWiget(id: Int): Ui[Any] = this <~ Transformer {\n    case widgetView: LauncherWidgetView if widgetView.widgetStatuses.widget.id == id =>\n      this <~ vgRemoveView(widgetView)\n  }\n\n  private[this] def partitionWidgets(): (Seq[LauncherWidgetView], Seq[LauncherWidgetView]) = {\n    this.children.collect {\n      case lwv: LauncherWidgetView => lwv\n    }.partition(lwv => statuses.idWidget.contains(lwv.widgetStatuses.widget.id))\n  }\n\n  private[this] def createRules: Ui[Any] = {\n    val spaceWidth  = (getWidth - (paddingDefault * 2)) / WidgetsOps.columns\n    val spaceHeight = (getHeight - (paddingDefault * 2)) / WidgetsOps.rows\n\n    def createView(horizontal: Boolean = true) =\n      (w[ImageView] <~\n        vWrapContent <~\n        vTag(ruleTag) <~\n        vBackground(new DottedDrawable(horizontal))).get\n\n    def horizontalRules(pos: Int) = {\n      val params = new LayoutParams(MATCH_PARENT, stroke)\n      params.leftMargin = paddingDefault\n      params.rightMargin = paddingDefault\n      params.topMargin = (pos * spaceHeight) + paddingDefault\n      vgAddViewByIndexParams(createView(), 0, params)\n    }\n\n    def verticalRules(pos: Int) = {\n      val params = new LayoutParams(stroke, MATCH_PARENT)\n      params.topMargin = paddingDefault\n      params.bottomMargin = paddingDefault\n      params.leftMargin = (pos * spaceWidth) + paddingDefault\n      vgAddViewByIndexParams(createView(horizontal = false), 0, params)\n    }\n\n    val tweaks = ((1 until WidgetsOps.rows) map horizontalRules) ++ ((1 until WidgetsOps.columns) map verticalRules)\n    val uis    = tweaks map (tweak => this <~ tweak)\n    Ui.sequence(uis: _*)\n  }\n\n  private[this] def removeRulesAnResizeFrame(): Ui[Any] = this <~ Transformer {\n    case i: ImageView if i.getTag == ruleTag => this <~ vgRemoveView(i)\n    case i: LauncherWidgetResizeFrame        => this <~ vgRemoveView(i)\n  }\n\n  private[this] def saveInfoInTag(cell: Cell, widget: Widget) =\n    vAddField(cellKey, cell) +\n      vAddField(widgetKey, widget)\n\n  private[this] def calculatePosition(x: Float, y: Float) = {\n    val w      = getWidth\n    val h      = getHeight\n    val spaceX = w / 5\n    val spaceY = h / 5\n    val sx     = (x / spaceX).toInt\n    val sy     = (y / spaceY).toInt\n    (sx, sy)\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/AppDrawerJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs\n\nimport cards.nine.app.ui.commons.{Jobs, RequestCodes}\nimport cards.nine.app.ui.launcher.jobs.uiactions.AppDrawerUiActions\nimport cards.nine.app.ui.launcher.types._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models\nimport cards.nine.models._\nimport cards.nine.models.types._\nimport macroid.ActivityContextWrapper\n\nclass AppDrawerJobs(val mainAppDrawerUiActions: AppDrawerUiActions)(\n    implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs { self =>\n\n  def loadSearch(query: String): TaskService[Unit] = {\n    for {\n      _      <- di.trackEventProcess.goToGooglePlayButton()\n      _      <- mainAppDrawerUiActions.showLoadingInGooglePlay()\n      result <- di.recommendationsProcess.searchApps(query)\n      _      <- mainAppDrawerUiActions.reloadSearchInDrawer(result)\n    } yield ()\n  }\n\n  def loadApps(appsMenuOption: AppsMenuOption): TaskService[Unit] = {\n    val getAppOrder = toGetAppOrder(appsMenuOption)\n    for {\n      _      <- di.trackEventProcess.goToAppDrawer()\n      _      <- di.trackEventProcess.goToApps()\n      result <- getLoadApps(getAppOrder)\n      (apps, counters) = result\n      _ <- mainAppDrawerUiActions\n        .reloadAppsInDrawer(apps = apps, getAppOrder = getAppOrder, counters = counters)\n    } yield ()\n  }\n\n  def loadContacts(contactsMenuOption: ContactsMenuOption): TaskService[Unit] = {\n\n    def getLoadContacts(\n        order: ContactsFilter): TaskService[(models.IterableContacts, Seq[TermCounter])] =\n      for {\n        iterableContacts <- di.deviceProcess.getIterableContacts(order)\n        counters         <- di.deviceProcess.getTermCountersForContacts(order)\n      } yield (iterableContacts, counters)\n\n    contactsMenuOption match {\n      case ContactsByLastCall =>\n        for {\n          _        <- di.trackEventProcess.goToContacts()\n          contacts <- di.deviceProcess.getLastCalls\n          _        <- mainAppDrawerUiActions.reloadLastCallContactsInDrawer(contacts)\n        } yield ()\n      case _ =>\n        val getContactFilter = toGetContactFilter(contactsMenuOption)\n        for {\n          _      <- di.trackEventProcess.goToContacts()\n          result <- getLoadContacts(getContactFilter)\n          (contacts, counters) = result\n          _ <- mainAppDrawerUiActions.reloadContactsInDrawer(contacts, counters)\n        } yield ()\n    }\n  }\n\n  def loadAppsByKeyword(keyword: String): TaskService[Unit] =\n    for {\n      apps <- di.deviceProcess.getIterableAppsByKeyWord(keyword, GetByName)\n      _    <- mainAppDrawerUiActions.reloadAppsInDrawer(apps)\n    } yield ()\n\n  def loadContactsByKeyword(keyword: String): TaskService[Unit] =\n    for {\n      contacts <- di.deviceProcess.getIterableContactsByKeyWord(keyword)\n      _        <- mainAppDrawerUiActions.reloadContactsInDrawer(contacts)\n    } yield ()\n\n  def requestReadContacts(): TaskService[Unit] =\n    di.userAccountsProcess.requestPermission(RequestCodes.contactsPermission, ReadContacts)\n\n  def requestReadCallLog(): TaskService[Unit] =\n    di.userAccountsProcess.requestPermission(RequestCodes.callLogPermission, ReadCallLog)\n\n  private[this] def getLoadApps(\n      order: GetAppOrder): TaskService[(IterableApplicationData, Seq[TermCounter])] =\n    for {\n      _            <- di.trackEventProcess.goToFiltersByButton(order.name)\n      iterableApps <- di.deviceProcess.getIterableApps(order)\n      counters     <- di.deviceProcess.getTermCountersForApps(order)\n    } yield (iterableApps, counters)\n\n  private[this] def toGetAppOrder(appsMenuOption: AppsMenuOption): GetAppOrder =\n    appsMenuOption match {\n      case AppsAlphabetical  => GetByName\n      case AppsByCategories  => GetByCategory\n      case AppsByLastInstall => GetByInstallDate\n    }\n\n  private[this] def toGetContactFilter(contactMenuOption: ContactsMenuOption): ContactsFilter =\n    contactMenuOption match {\n      case ContactsFavorites => FavoriteContacts\n      case _                 => AllContacts\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/DragJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs\n\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.ui.commons.Constants._\nimport cards.nine.app.ui.commons.action_filters.MomentReloadedActionFilter\nimport cards.nine.app.ui.commons.{BroadAction, JobException, Jobs}\nimport cards.nine.app.ui.components.models.{CollectionsWorkSpace, LauncherData}\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.jobs.uiactions._\nimport cards.nine.app.ui.launcher.{AddItemMode, ReorderMode}\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.ops.SeqOps._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.Application.ApplicationDataOps\nimport cards.nine.models._\nimport cards.nine.models.types._\nimport cats.implicits._\nimport macroid.ActivityContextWrapper\n\nclass DragJobs(\n    val mainAppDrawerUiActions: AppDrawerUiActions,\n    val navigationUiActions: NavigationUiActions,\n    val dockAppsUiActions: DockAppsUiActions,\n    val workspaceUiActions: WorkspaceUiActions,\n    val dragUiActions: DragUiActions)(implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with Conversions {\n\n  def startAddItemToCollection(app: ApplicationData): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.addAppToCollection(app.packageName)\n      _ <- startAddItemToCollection(app.toCardData)\n    } yield ()\n\n  def startAddItemToCollection(contact: Contact): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.addContactToCollection()\n      _ <- startAddItemToCollection(toCardData(contact))\n    } yield ()\n\n  def startAddItemToCollection(dockAppData: DockAppData): TaskService[Unit] =\n    toCardData(dockAppData) match {\n      case Some(cardType) =>\n        for {\n          _ <- di.deviceProcess.deleteDockAppByPosition(dockAppData.position)\n          _ <- startAddItemToCollectionFromDockApps(cardType)\n        } yield ()\n      case _ => TaskService.left(JobException(\"Dock type unsupported\"))\n    }\n\n  def draggingAddItemTo(position: Int): TaskService[Unit] = TaskService.right {\n    statuses = statuses.updateCurrentPosition(position)\n  }\n\n  def draggingAddItemToPreviousScreen(position: Int): TaskService[Unit] =\n    for {\n      _ <- dragUiActions.goToPreviousScreenAddingItem()\n      _ <- TaskService.right(statuses.updateCurrentPosition(position))\n    } yield ()\n\n  def draggingAddItemToNextScreen(position: Int): TaskService[Unit] =\n    for {\n      _ <- dragUiActions.goToNextScreenAddingItem()\n      _ <- TaskService.right(statuses.updateCurrentPosition(position))\n    } yield ()\n\n  def endAddItemToCollection(): TaskService[Unit] = {\n    val collectionTasks =\n      (dragUiActions.dom.getCollection(statuses.currentDraggingPosition), statuses.cardAddItemMode) match {\n        case (Some(collection: Collection), Some(card: CardData)) =>\n          for {\n            _ <- di.collectionProcess.addCards(collection.id, Seq(card))\n            _ <- navigationUiActions.showAddItemMessage(collection.name)\n            _ <- sendBroadCastTask(BroadAction(MomentReloadedActionFilter.action))\n          } yield ()\n        case _ => TaskService.empty\n      }\n    for {\n      _ <- collectionTasks\n      _ <- TaskService.right(statuses = statuses.reset())\n      _ <- dragUiActions.endAddItem()\n    } yield ()\n  }\n\n  def changePositionDockApp(from: Int, to: Int): TaskService[Unit] = {\n    for {\n      dockApps <- di.deviceProcess.getDockApps\n      maybeDockAppFrom = dockApps find (_.position == from)\n      _ <- maybeDockAppFrom match {\n        case Some(dockApp) =>\n          di.deviceProcess.createOrUpdateDockApp(\n            dockApp.name,\n            dockApp.dockType,\n            dockApp.intent,\n            dockApp.imagePath,\n            to) *>\n            dockAppsUiActions.reloadDockApps(dockApp.toData.copy(position = to))\n        case _ => TaskService.empty\n      }\n    } yield ()\n  }\n\n  def endAddItemToDockApp(position: Int): TaskService[Unit] = {\n\n    def createOrUpdateDockApp(card: CardData, dockType: DockType) =\n      for {\n        _ <- di.deviceProcess.createOrUpdateDockApp(\n          card.term,\n          dockType,\n          card.intent,\n          card.imagePath getOrElse \"\",\n          position)\n        _ <- dockAppsUiActions.reloadDockApps(\n          DockAppData(card.term, dockType, card.intent, card.imagePath getOrElse \"\", position))\n      } yield ()\n\n    val dockAppsTasks = statuses.cardAddItemMode match {\n      case Some(card: CardData) =>\n        card.cardType match {\n          case AppCardType     => createOrUpdateDockApp(card, AppDockType)\n          case ContactCardType => createOrUpdateDockApp(card, ContactDockType)\n          case _               => navigationUiActions.showContactUsError()\n        }\n      case _ => navigationUiActions.showContactUsError()\n    }\n    for {\n      _ <- dockAppsTasks\n      _ <- TaskService.right(statuses = statuses.reset())\n      _ <- dragUiActions.endAddItem()\n    } yield ()\n  }\n\n  def endAddItem(): TaskService[Unit] =\n    if (statuses.mode == AddItemMode) {\n      statuses = statuses.reset()\n      dragUiActions.endAddItem()\n    } else {\n      TaskService.empty\n    }\n\n  def uninstallInAddItem(): TaskService[Unit] = {\n    val launchTask = statuses.cardAddItemMode match {\n      case Some(card: CardData) if card.cardType == AppCardType =>\n        card.packageName match {\n          case Some(packageName) =>\n            di.launcherExecutorProcess.launchUninstall(packageName)\n          case _ => TaskService.empty\n        }\n      case _ => TaskService.empty\n    }\n    for {\n      _ <- launchTask\n      _ <- TaskService.right(statuses = statuses.reset())\n      _ <- dragUiActions.endAddItem()\n    } yield ()\n  }\n\n  def settingsInAddItem(): TaskService[Unit] = {\n    val launchTask = statuses.cardAddItemMode match {\n      case Some(card: CardData) if card.cardType == AppCardType =>\n        card.packageName match {\n          case Some(packageName) =>\n            di.launcherExecutorProcess.launchSettings(packageName)\n          case _ => TaskService.empty\n        }\n      case _ => TaskService.empty\n    }\n    for {\n      _ <- launchTask\n      _ <- TaskService.right(statuses = statuses.reset())\n      _ <- dragUiActions.endAddItem()\n    } yield ()\n  }\n\n  def startReorder(maybeCollection: Option[Collection], position: Int): TaskService[Unit] =\n    maybeCollection match {\n      case Some(collection) =>\n        for {\n          _ <- TaskService.right(statuses = statuses.startReorder(collection, position))\n          _ <- dragUiActions.startReorder()\n        } yield ()\n      case _ => navigationUiActions.showContactUsError()\n    }\n\n  def draggingReorderTo(position: Int): TaskService[Unit] = TaskService.right {\n    statuses = statuses.updateCurrentPosition(position)\n  }\n\n  def draggingReorderToNextScreen(position: Int): TaskService[Unit] =\n    for {\n      _ <- dragUiActions.goToNextScreenReordering()\n      _ <- TaskService.right(statuses = statuses.updateCurrentPosition(position))\n    } yield ()\n\n  def draggingReorderToPreviousScreen(position: Int): TaskService[Unit] =\n    for {\n      _ <- dragUiActions.goToPreviousScreenReordering()\n      _ <- TaskService.right(statuses = statuses.updateCurrentPosition(position))\n    } yield ()\n\n  def dropReorder(): TaskService[Unit] =\n    if (statuses.mode == ReorderMode) {\n\n      def reorderPositions() = {\n        val from = statuses.startPositionReorderMode\n        val to   = statuses.currentDraggingPosition\n        if (from != to) {\n          for {\n            _ <- di.trackEventProcess.reorderCollection()\n            _ <- di.collectionProcess.reorderCollection(from, to)\n            _ <- workspaceUiActions.reloadWorkspaces(reorderCollectionsInCurrentData(from, to))\n          } yield ()\n        } else {\n          workspaceUiActions.reloadWorkspaces(reloadCollectionsInCurrentData)\n        }\n      }\n\n      for {\n        _ <- dragUiActions.endReorder()\n        _ <- reorderPositions()\n        _ <- TaskService.right(statuses = statuses.reset())\n      } yield ()\n    } else {\n      TaskService.empty\n    }\n\n  def dropReorderException() =\n    workspaceUiActions.reloadWorkspaces(reloadCollectionsInCurrentData) *>\n      navigationUiActions.showContactUsError()\n\n  def removeCollectionInReorderMode(): TaskService[Unit] =\n    statuses.collectionReorderMode match {\n      case Some(collection) =>\n        if (dragUiActions.dom.canRemoveCollections) {\n          navigationUiActions.showDialogForRemoveCollection(collection)\n        } else {\n          navigationUiActions.showMinimumOneCollectionMessage()\n        }\n      case _ => navigationUiActions.showContactUsError()\n    }\n\n  private[this] def startAddItemToCollection(card: CardData): TaskService[Unit] = {\n    statuses = statuses.startAddItem(card)\n    for {\n      _ <- mainAppDrawerUiActions.close()\n      _ <- navigationUiActions\n        .goToCollectionWorkspace()\n        .resolveIf(!mainAppDrawerUiActions.dom.isCollectionWorkspace, ())\n      _ <- dragUiActions.startAddItem(card.cardType)\n    } yield ()\n  }\n\n  private[this] def startAddItemToCollectionFromDockApps(card: CardData): TaskService[Unit] = {\n    statuses = statuses.startAddItem(card)\n    for {\n      _ <- navigationUiActions\n        .goToCollectionWorkspace()\n        .resolveIf(!mainAppDrawerUiActions.dom.isCollectionWorkspace, ())\n      _ <- dragUiActions.startAddItemFromDockApp(card.cardType)\n    } yield ()\n  }\n\n  private[this] def reorderCollectionsInCurrentData(from: Int, to: Int): Seq[LauncherData] = {\n    val cols = dragUiActions.dom.getData flatMap (_.collections)\n    val collections = cols.reorder(from, to).zipWithIndex map {\n      case (collection, index) => collection.copy(position = index)\n    }\n    createLauncherDataCollections(collections)\n  }\n\n  private[this] def reloadCollectionsInCurrentData: Seq[LauncherData] = {\n    val collections = dragUiActions.dom.getData flatMap (_.collections)\n    createLauncherDataCollections(collections)\n  }\n\n  private[this] def createLauncherDataCollections(\n      collections: Seq[Collection]): Seq[LauncherData] = {\n    collections.grouped(numSpaces).toList.zipWithIndex map {\n      case (data, index) =>\n        LauncherData(CollectionsWorkSpace, collections = data, positionByType = index)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/LauncherJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs\n\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.receivers.moments.MomentBroadcastReceiver\nimport cards.nine.app.ui.commons.Constants._\nimport cards.nine.app.ui.commons.action_filters.{\n  MomentForceBestAvailableActionFilter,\n  MomentReloadedActionFilter\n}\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.commons.states.MomentState\nimport cards.nine.app.ui.components.models.{\n  CollectionsWorkSpace,\n  LauncherData,\n  LauncherMoment,\n  MomentWorkSpace\n}\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.exceptions.{ChangeMomentException, LoadDataException}\nimport cards.nine.app.ui.launcher.jobs.uiactions._\nimport cards.nine.app.ui.preferences.commons._\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.{TaskService, _}\nimport cards.nine.models.types.{NineCardsMoment, UnknownCondition, _}\nimport cards.nine.models.{Collection, DockApp, Moment, User}\nimport cats.implicits._\nimport macroid.ActivityContextWrapper\nimport monix.eval.Task\n\nclass LauncherJobs(\n    val mainLauncherUiActions: LauncherUiActions,\n    val workspaceUiActions: WorkspaceUiActions,\n    val menuDrawersUiActions: MenuDrawersUiActions,\n    val appDrawerUiActions: AppDrawerUiActions,\n    val navigationUiActions: NavigationUiActions,\n    val dockAppsUiActions: DockAppsUiActions,\n    val topBarUiActions: TopBarUiActions,\n    val widgetUiActions: WidgetUiActions,\n    val dragUiActions: DragUiActions)(implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with AppNineCardsIntentConversions { self =>\n\n  lazy val momentState = new MomentState\n\n  def momentBroadcastReceiver = new MomentBroadcastReceiver\n\n  val defaultPage = 1\n\n  def initialize(): TaskService[Unit] = {\n    def initServices: TaskService[Unit] =\n      di.externalServicesProcess.initializeStrictMode *>\n        di.externalServicesProcess.initializeCrashlytics *>\n        di.externalServicesProcess.initializeFirebase *>\n        di.externalServicesProcess.initializeStetho *>\n        di.externalServicesProcess.initializeFlowUp.resolveIf(IsFlowUpActive.readValue, ()) *>\n        di.externalServicesProcess.initializeApptentive\n\n    def initAllUiActions(): TaskService[Unit] =\n      widgetUiActions.initialize() *>\n        workspaceUiActions.initialize() *>\n        menuDrawersUiActions.initialize() *>\n        appDrawerUiActions.initialize() *>\n        topBarUiActions.initialize() *>\n        mainLauncherUiActions.initialize()\n\n    for {\n      _     <- mainLauncherUiActions.initialize()\n      theme <- getThemeTask\n      _     <- TaskService.right(statuses = statuses.copy(theme = theme))\n      _     <- initAllUiActions()\n      _     <- initServices\n      _     <- di.userProcess.register\n    } yield ()\n  }\n\n  def resume(): TaskService[Unit] =\n    (if (mainLauncherUiActions.dom.isEmptyCollections) {\n       loadLauncherInfo().resolveLeft {\n         case uiException: UiException => Left(uiException)\n         case ex                       => Left(LoadDataException(\"Data not loaded\", Option(ex)))\n       }\n     } else {\n       changeMomentIfIsAvailable(force = false).resolveLeft(exception =>\n         Left(ChangeMomentException(\"Exception changing moment\", Option(exception))))\n     }) *>\n      di.observerRegister.registerObserverTask() *>\n      updateWeather().resolveIf(ShowWeatherMoment.readValue, ())\n\n  def registerFence(): TaskService[Unit] =\n    di.recognitionProcess.registerFenceUpdates(\n      action = MomentBroadcastReceiver.momentFenceAction,\n      receiver = momentBroadcastReceiver)\n\n  def unregisterFence(): TaskService[Unit] =\n    di.recognitionProcess.unregisterFenceUpdates(MomentBroadcastReceiver.momentFenceAction)\n\n  def reloadFence(): TaskService[Unit] =\n    for {\n      _ <- unregisterFence()\n      _ <- registerFence()\n    } yield ()\n\n  def pause(): TaskService[Unit] = di.observerRegister.unregisterObserverTask()\n\n  def destroy(): TaskService[Unit] = widgetUiActions.destroy()\n\n  def reloadAppsMomentBar(): TaskService[Unit] = {\n\n    def selectMoment(moments: Seq[Moment]): Option[Moment] =\n      for {\n        currentMomentType <- mainLauncherUiActions.dom.getCurrentMomentType\n        moment            <- moments find (_.momentType == currentMomentType)\n      } yield moment\n\n    def getCollectionById(collectionId: Option[Int]): TaskService[Option[Collection]] =\n      collectionId match {\n        case Some(id) => di.collectionProcess.getCollectionById(id)\n        case _        => TaskService.right(None)\n      }\n\n    for {\n      moments <- di.momentProcess.getMoments\n      moment = selectMoment(moments)\n      collection <- getCollectionById(moment flatMap (_.collectionId))\n      launcherMoment = LauncherMoment(moment map (_.momentType), collection)\n      _ <- menuDrawersUiActions.reloadBarMoment(launcherMoment)\n    } yield ()\n  }\n\n  def loadLauncherInfo(): TaskService[Unit] = {\n\n    def getCollectionMoment(moment: Option[Moment], collections: Seq[Collection]) =\n      for {\n        m            <- moment\n        collectionId <- m.collectionId\n        collection   <- collections find (_.id == collectionId)\n      } yield collection\n\n    def getMoment = momentState.getPersistMoment match {\n      case Some(moment) => di.momentProcess.fetchMomentByType(moment)\n      case _            => di.momentProcess.getBestAvailableMoment()\n    }\n\n    def getLauncherInfo: TaskService[(Seq[Collection], Seq[DockApp], Option[Moment])] =\n      (di.collectionProcess.getCollections |@| di.deviceProcess.getDockApps |@| getMoment).tupled\n\n    def loadData(collections: Seq[Collection], apps: Seq[DockApp], moment: Option[Moment]) = {\n      val collectionMoment = getCollectionMoment(moment, collections)\n      val launcherMoment =\n        LauncherMoment(moment map (_.momentType), collectionMoment)\n      val data = LauncherData(MomentWorkSpace, Option(launcherMoment)) +: createLauncherDataCollections(\n          collections)\n      for {\n        _ <- workspaceUiActions.loadLauncherInfo(data)\n        _ <- dockAppsUiActions.loadDockApps(apps map (_.toData))\n        _ <- topBarUiActions.loadBar(data)\n        _ <- menuDrawersUiActions.reloadBarMoment(launcherMoment)\n      } yield ()\n    }\n\n    def loadUser() =\n      for {\n        maybeUser <- di.userProcess.getUser.resolveAsOption\n        _ <- maybeUser match {\n          case Some(user) =>\n            menuDrawersUiActions.loadUserProfileMenu(\n              maybeEmail = user.email,\n              maybeName = user.userProfile.name,\n              maybeAvatarUrl = user.userProfile.avatar,\n              maybeCoverUrl = user.userProfile.cover)\n          case _ => TaskService.empty\n        }\n      } yield ()\n\n    for {\n      result <- getLauncherInfo\n      _ <- result match {\n        case (Nil, _, _) =>\n          TaskService.left(LoadDataException(\"There isn't collections\"))\n        case (collections, apps, moment) =>\n          loadData(collections, apps, moment) *> loadUser()\n      }\n    } yield ()\n  }\n\n  def changeMomentIfIsAvailable(\n      force: Boolean,\n      fenceKey: Option[String] = None): TaskService[Unit] = {\n\n    def getCollection(moment: Option[Moment]): TaskService[Option[Collection]] = {\n      val collectionId = moment flatMap (_.collectionId)\n      collectionId map di.collectionProcess.getCollectionById getOrElse TaskService.right(None)\n    }\n\n    def headphoneKey: Option[Boolean] = fenceKey match {\n      case Some(HeadphonesFence.keyIn)  => Some(true)\n      case Some(HeadphonesFence.keyOut) => Some(false)\n      case _                            => None\n    }\n\n    def activityKey: Option[KindActivity] = fenceKey match {\n      case Some(InVehicleFence.key) => Some(InVehicleActivity)\n      case _                        => None\n    }\n\n    val canChangeMoment = force || momentState.nonPersist\n\n    for {\n      moment <- di.momentProcess\n        .getBestAvailableMoment(maybeHeadphones = headphoneKey, maybeActivity = activityKey)\n      collection <- getCollection(moment)\n      currentMomentType = mainLauncherUiActions.dom.getCurrentMomentType\n      momentType        = moment map (_.momentType)\n      sameMoment        = currentMomentType == momentType\n      _ <- (sameMoment, canChangeMoment) match {\n        case (false, true) =>\n          val launcherMoment =\n            LauncherMoment(moment map (_.momentType), collection)\n          val data = LauncherData(MomentWorkSpace, Option(launcherMoment))\n          workspaceUiActions.reloadMoment(data)\n        case _ => TaskService.empty\n      }\n      _ <- sendBroadCastTask(BroadAction(MomentReloadedActionFilter.action))\n    } yield ()\n  }\n\n  def changeMoment(momentId: Int): TaskService[Unit] = {\n    for {\n      moment <- di.momentProcess\n        .findMoment(momentId)\n        .resolveOption(s\"Moment id $momentId not found\")\n      _ <- di.trackEventProcess.changeMoment(moment.momentType.name)\n      _ <- TaskService.right(momentState.persist(moment.momentType))\n      collection <- moment.collectionId match {\n        case Some(collectionId: Int) =>\n          di.collectionProcess.getCollectionById(collectionId)\n        case _ => TaskService.right(None)\n      }\n      data = LauncherData(\n        MomentWorkSpace,\n        Option(LauncherMoment(Option(moment.momentType), collection)))\n      _ <- workspaceUiActions.reloadMoment(data)\n      _ <- sendBroadCastTask(BroadAction(MomentReloadedActionFilter.action))\n    } yield ()\n  }\n\n  def cleanPersistedMoment(): TaskService[Unit] = {\n    momentState.clean()\n    for {\n      _ <- di.trackEventProcess.unpinMoment()\n      _ <- sendBroadCastTask(BroadAction(MomentForceBestAvailableActionFilter.action))\n    } yield ()\n\n  }\n\n  def reloadCollection(collectionId: Int): TaskService[Unit] =\n    for {\n      collection <- di.collectionProcess\n        .getCollectionById(collectionId)\n        .resolveOption(\"Collection Id not found in reload collection\")\n      _ <- addCollection(collection)\n    } yield ()\n\n  def addCollection(collection: Collection): TaskService[Unit] = {\n    addCollectionToCurrentData(collection) match {\n      case Some((page: Int, data: Seq[LauncherData])) =>\n        for {\n          _ <- workspaceUiActions.reloadWorkspaces(data, Some(page))\n          _ <- sendBroadCastTask(BroadAction(MomentReloadedActionFilter.action))\n        } yield ()\n      case _ => TaskService.empty\n    }\n  }\n\n  def updateCollection(collection: Collection): TaskService[Unit] = {\n    val cols = mainLauncherUiActions.dom.getData flatMap (_.collections)\n    cols.lift(collection.position) match {\n      case Some(_) =>\n        val collections = cols.updated(collection.position, collection)\n        val newCols     = createLauncherDataCollections(collections)\n        workspaceUiActions.reloadWorkspaces(newCols)\n      case _ => navigationUiActions.showContactUsError()\n    }\n  }\n\n  def removeCollection(collection: Collection): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.deleteCollection(collection.name)\n      _ <- di.collectionProcess.deleteCollection(collection.id)\n      (page, data) = removeCollectionToCurrentData(collection.id)\n      _ <- workspaceUiActions.reloadWorkspaces(data, Option(page))\n      _ <- sendBroadCastTask(BroadAction(MomentReloadedActionFilter.action))\n    } yield ()\n\n  def removeMomentDialog(moment: NineCardsMoment, momentId: Int): TaskService[Unit] =\n    moment.isDefault match {\n      case true => navigationUiActions.showCantRemoveOutAndAboutMessage()\n      case _    => navigationUiActions.showDialogForRemoveMoment(momentId)\n    }\n\n  def removeMoment(momentId: Int): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.deleteMoment()\n      _ <- di.momentProcess.deleteMoment(momentId)\n      _ <- cleanPersistedMoment()\n      _ <- reloadFence()\n    } yield ()\n\n  def preferencesChanged(changedPreferences: Array[String]): TaskService[Unit] = {\n\n    def needToRecreate(array: Array[String]): Boolean =\n      array\n        .intersect(\n          Seq(\n            Theme.name,\n            IconsSize.name,\n            FontSize.name,\n            WallpaperAnimation.name,\n            AppDrawerSelectItemsInScroller.name))\n        .nonEmpty\n\n    def uiAction(prefKey: String): TaskService[Unit] = prefKey match {\n      case ShowMicSearchMoment.name => topBarUiActions.reloadMomentTopBar()\n      case ShowWeatherMoment.name   => topBarUiActions.reloadMomentTopBar()\n      case GoogleLogo.name          => topBarUiActions.reloadTopBar()\n      case _                        => TaskService.empty\n    }\n\n    Option(changedPreferences) match {\n      case Some(array) if array.nonEmpty =>\n        if (needToRecreate(array)) {\n          mainLauncherUiActions.reloadAllViews()\n        } else {\n          val tasks = array.map(ar => uiAction(ar).value).toSeq\n          TaskService(Task.gatherUnordered(tasks) map (_ => Right((): Unit)))\n        }\n      case _ => TaskService.empty\n    }\n  }\n\n  def requestPermissionsResult(\n      requestCode: Int,\n      permissions: Array[String],\n      grantResults: Array[Int]): TaskService[Unit] = {\n\n    def serviceAction(result: Seq[PermissionResult]): TaskService[Unit] =\n      requestCode match {\n        case RequestCodes.contactsPermission if result.exists(_.hasPermission(ReadContacts)) =>\n          appDrawerUiActions.reloadContacts()\n        case RequestCodes.callLogPermission if result.exists(_.hasPermission(ReadCallLog)) =>\n          appDrawerUiActions.reloadContacts()\n        case RequestCodes.phoneCallPermission if result.exists(_.hasPermission(CallPhone)) =>\n          statuses.lastPhone match {\n            case Some(phone) =>\n              statuses = statuses.copy(lastPhone = None)\n              di.launcherExecutorProcess.execute(phoneToNineCardIntent(None, phone))\n            case _ => TaskService.right((): Unit)\n          }\n        case RequestCodes.contactsPermission =>\n          for {\n            _ <- appDrawerUiActions.reloadApps()\n            _ <- navigationUiActions.showContactPermissionError(\n              () =>\n                di.userAccountsProcess\n                  .requestPermission(RequestCodes.contactsPermission, ReadContacts)\n                  .resolveAsync())\n          } yield ()\n        case RequestCodes.callLogPermission =>\n          for {\n            _ <- appDrawerUiActions.reloadApps()\n            _ <- navigationUiActions.showCallPermissionError(\n              () =>\n                di.userAccountsProcess\n                  .requestPermission(RequestCodes.callLogPermission, ReadCallLog)\n                  .resolveAsync())\n          } yield ()\n        case RequestCodes.phoneCallPermission =>\n          statuses.lastPhone match {\n            case Some(phone) =>\n              statuses = statuses.copy(lastPhone = None)\n              for {\n                _ <- di.launcherExecutorProcess.launchDial(Option(phone))\n                _ <- navigationUiActions.showNoPhoneCallPermissionError()\n              } yield ()\n            case _ => TaskService.empty\n          }\n        case RequestCodes.locationPermission if result.exists(_.hasPermission(FineLocation)) =>\n          for {\n            _ <- updateWeather().resolveIf(ShowWeatherMoment.readValue, ())\n            _ <- di.launcherExecutorProcess.launchGoogleWeather\n          } yield ()\n        case _ => TaskService.empty\n      }\n\n    for {\n      result <- di.userAccountsProcess.parsePermissionsRequestResult(permissions, grantResults)\n      _      <- serviceAction(result)\n    } yield ()\n\n  }\n\n  private[this] def removeCollectionToCurrentData(collectionId: Int): (Int, Seq[LauncherData]) = {\n    val currentData =\n      mainLauncherUiActions.dom.getData.filter(_.workSpaceType == CollectionsWorkSpace)\n\n    // We remove a collection in sequence and fix positions\n    val collections = (currentData flatMap (_.collections\n        .filterNot(_.id == collectionId))).zipWithIndex map {\n      case (col, index) => col.copy(position = index)\n    }\n\n    val maybeWorkspaceCollection = currentData find (_.collections.exists(_.id == collectionId))\n    val maybePage                = maybeWorkspaceCollection map currentData.indexOf\n\n    val newData = createLauncherDataCollections(collections)\n\n    val page = maybePage map { page =>\n      if (newData.isDefinedAt(page)) page else newData.length - 1\n    } getOrElse defaultPage\n\n    (page, newData)\n  }\n\n  private[this] def updateWeather(): TaskService[Unit] =\n    for {\n      weather <- di.recognitionProcess.getWeather\n      _       <- workspaceUiActions.showWeather(weather.conditions.headOption getOrElse UnknownCondition)\n    } yield ()\n\n  private[this] def addCollectionToCurrentData(\n      collection: Collection): Option[(Int, Seq[LauncherData])] = {\n    val currentData =\n      mainLauncherUiActions.dom.getData.filter(_.workSpaceType == CollectionsWorkSpace)\n    currentData.lastOption map { data =>\n      val lastWorkspaceHasSpace = data.collections.size < numSpaces\n      val newData = if (lastWorkspaceHasSpace) {\n        currentData.dropRight(1) :+ data.copy(collections = data.collections :+ collection)\n      } else {\n        val newPosition =\n          currentData.count(_.workSpaceType == CollectionsWorkSpace)\n        currentData :+ LauncherData(\n          CollectionsWorkSpace,\n          collections = Seq(collection),\n          positionByType = newPosition)\n      }\n      val page = newData.size - 1\n      (page, newData)\n    }\n  }\n\n  private[this] def createLauncherDataCollections(\n      collections: Seq[Collection]): Seq[LauncherData] = {\n    collections.grouped(numSpaces).toList.zipWithIndex map {\n      case (data, index) =>\n        LauncherData(CollectionsWorkSpace, collections = data, positionByType = index)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/NavigationJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs\n\nimport cats.implicits._\nimport android.graphics.Point\nimport android.os.Bundle\nimport cards.nine.app.commons.AppNineCardsIntentConversions\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\nimport cards.nine.app.ui.commons.dialogs.createoreditcollection.CreateOrEditCollectionFragment\nimport cards.nine.app.ui.commons.dialogs.editmoment.EditMomentFragment\nimport cards.nine.app.ui.commons.dialogs.widgets.WidgetsFragment\nimport cards.nine.app.ui.commons.{Jobs, RequestCodes}\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.jobs.uiactions.{\n  AppDrawerUiActions,\n  MenuDrawersUiActions,\n  NavigationUiActions,\n  WidgetUiActions\n}\nimport cards.nine.app.ui.launcher.{EditWidgetsMode, NormalMode}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.{TaskService, _}\nimport cards.nine.models._\nimport cards.nine.models.types._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.extras.ResourcesExtras._\nimport macroid.{ActivityContextWrapper, ContextWrapper}\n\nclass NavigationJobs(\n    val navigationUiActions: NavigationUiActions,\n    val appDrawerUiActions: AppDrawerUiActions,\n    val menuDrawersUiActions: MenuDrawersUiActions,\n    val widgetUiActions: WidgetUiActions)(implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with NineCardsIntentConversions\n    with AppNineCardsIntentConversions {\n\n  def openMenu(): TaskService[Unit] = menuDrawersUiActions.openMenu()\n\n  def launchCreateOrCollection(collectionId: Option[Int] = None): TaskService[Unit] = {\n    val collectionMap: Map[String, String] = collectionId match {\n      case Some(id) =>\n        Map(CreateOrEditCollectionFragment.collectionId -> id.toString)\n      case _ => Map.empty\n    }\n    val bundle = createBundle(getColor(R.color.collection_fab_button_item_1), collectionMap)\n    navigationUiActions.launchCreateOrCollection(bundle)\n  }\n\n  def launchPrivateCollection(): TaskService[Unit] = {\n    val bundle = createBundle(getColor(R.color.collection_fab_button_item_2))\n    navigationUiActions.launchPrivateCollection(bundle)\n  }\n\n  def launchPublicCollection(): TaskService[Unit] = {\n    val bundle = createBundle(getColor(R.color.collection_fab_button_item_3))\n    navigationUiActions.launchPublicCollection(bundle)\n  }\n\n  def launchAddMoment(): TaskService[Unit] = {\n    val bundle = createBundle(getColor(R.color.collection_fab_button_item_3))\n    navigationUiActions.launchAddMoment(bundle)\n  }\n\n  def launchEditMoment(moment: String): TaskService[Unit] = {\n    val momentMap = Map(EditMomentFragment.momentKey -> moment)\n    val bundle =\n      createBundle(getColor(R.color.collection_fab_button_item_1), momentMap)\n    navigationUiActions.launchEditMoment(bundle)\n  }\n\n  def launchWidgets(): TaskService[Unit] = {\n    val widthContent  = navigationUiActions.dom.workspaces.getWidth\n    val heightContent = navigationUiActions.dom.workspaces.getHeight\n    val map = Map(\n      WidgetsFragment.widgetContentWidth  -> widthContent.toString,\n      WidgetsFragment.widgetContentHeight -> heightContent.toString\n    )\n    val bundle = createBundle(getColor(R.color.primary), map)\n    navigationUiActions.launchWidgets(bundle)\n  }\n\n  def goToCollection(maybeCollection: Option[Collection], point: Point): TaskService[Unit] = {\n\n    def openCollection(collection: Collection): TaskService[Unit] =\n      for {\n        _ <- di.trackEventProcess.openCollectionTitle(collection.name)\n        _ <- di.trackEventProcess.openCollectionOrder(collection.position)\n        _ <- navigationUiActions.goToCollection(collection, point)\n      } yield ()\n\n    for {\n      _ <- di.trackEventProcess.useNavigationBar()\n      _ <- maybeCollection match {\n        case Some(collection) => openCollection(collection)\n        case _                => navigationUiActions.showContactUsError()\n      }\n    } yield ()\n  }\n\n  def openApp(app: ApplicationData): TaskService[Unit] =\n    if (navigationUiActions.dom.isDrawerTabsOpened) {\n      appDrawerUiActions.closeTabs()\n    } else {\n      for {\n        _ <- di.trackEventProcess.openAppFromAppDrawer(app.packageName, AppCategory(app.category))\n        _ <- di.launcherExecutorProcess.execute(toNineCardIntent(app))\n      } yield ()\n    }\n\n  def openContact(contact: Contact): TaskService[Unit] =\n    if (navigationUiActions.dom.isDrawerTabsOpened) {\n      appDrawerUiActions.closeTabs()\n    } else {\n      di.launcherExecutorProcess.executeContact(contact.lookupKey)\n    }\n\n  def openLastCall(number: String): TaskService[Unit] =\n    if (navigationUiActions.dom.isDrawerTabsOpened) {\n      appDrawerUiActions.closeTabs()\n    } else {\n      di.launcherExecutorProcess.execute(phoneToNineCardIntent(None, number))\n    }\n\n  def openMomentIntent(card: Card, moment: Option[NineCardsMoment]): TaskService[Unit] = {\n\n    def trackAppMoment(packageName: String, moment: NineCardsMoment) =\n      for {\n        _ <- di.trackEventProcess.openAppFromCollection(packageName, MomentCategory(moment))\n        _ <- di.trackEventProcess.openApplicationByMoment(moment.name)\n      } yield ()\n\n    for {\n      _ <- (card.packageName, moment) match {\n        case (Some(packageName), Some(m)) => trackAppMoment(packageName, m)\n        case _                            => TaskService.empty\n      }\n      _ <- menuDrawersUiActions.closeAppsMoment()\n      _ <- di.launcherExecutorProcess.execute(card.intent)\n    } yield ()\n  }\n\n  def openMomentIntentException(maybePhone: Option[String]): TaskService[Unit] = {\n    statuses = statuses.copy(lastPhone = maybePhone)\n    di.userAccountsProcess.requestPermission(RequestCodes.phoneCallPermission, CallPhone)\n  }\n\n  def openDockApp(app: DockAppData): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.openDockAppTitle(app.name)\n      _ <- di.trackEventProcess.openDockAppOrder(app.position)\n      _ <- di.launcherExecutorProcess.execute(app.intent)\n    } yield ()\n\n  def launchSearch(): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.usingSearchByKeyboard()\n      _ <- di.launcherExecutorProcess.launchSearch\n    } yield ()\n\n  def launchVoiceSearch(): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.usingSearchByVoice()\n      _ <- di.launcherExecutorProcess.launchVoiceSearch\n    } yield ()\n\n  def launchGooglePlay(packageName: String): TaskService[Unit] =\n    di.launcherExecutorProcess.launchGooglePlay(packageName)\n\n  def launchGoogleWeather(): TaskService[Unit] =\n    for {\n      _      <- di.trackEventProcess.goToWeather()\n      result <- di.userAccountsProcess.havePermission(types.FineLocation)\n      _ <- if (result.hasPermission(types.FineLocation)) {\n        di.launcherExecutorProcess.launchGoogleWeather\n      } else {\n        di.userAccountsProcess.requestPermission(RequestCodes.locationPermission, FineLocation)\n      }\n    } yield ()\n\n  def launchPlayStore(): TaskService[Unit] =\n    di.launcherExecutorProcess.launchPlayStore\n\n  def launchDial(): TaskService[Unit] =\n    di.launcherExecutorProcess.launchDial(phoneNumber = None)\n\n  def goToChangeMoment(): TaskService[Unit] =\n    for {\n      moments <- di.momentProcess.getMoments\n      _       <- navigationUiActions.showSelectMomentDialog(moments)\n    } yield ()\n\n  def goToMenuOption(itemId: Int): TaskService[Unit] = {\n    itemId match {\n      case R.id.menu_collections =>\n        di.trackEventProcess.goToCollectionsByMenu() *> navigationUiActions\n          .goToCollectionWorkspace()\n      case R.id.menu_moments =>\n        di.trackEventProcess.goToMomentsByMenu() *> navigationUiActions.goToMomentWorkspace()\n      case R.id.menu_profile =>\n        di.trackEventProcess.goToProfileByMenu() *> navigationUiActions.goToProfile()\n      case R.id.menu_wallpaper => navigationUiActions.launchWallpaper()\n      case R.id.menu_settings  => navigationUiActions.launchSettings()\n      case R.id.menu_widget    => launchWidgets()\n      case _                   => TaskService.empty\n    }\n  }\n\n  private[this] def createBundle(color: Int, map: Map[String, String] = Map.empty)(\n      implicit contextWrapper: ContextWrapper): Bundle = {\n    val args = new Bundle()\n    map foreach {\n      case (key, value) => args.putString(key, value)\n    }\n    args.putInt(BaseActionFragment.colorPrimary, color)\n    args\n  }\n\n  protected def getColor(res: Int): Int = resGetColor(res)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/WidgetsJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs\n\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.app.ui.commons.ops.WidgetsOps\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.exceptions.SpaceException\nimport cards.nine.app.ui.launcher.holders._\nimport cards.nine.app.ui.launcher.jobs.uiactions.{NavigationUiActions, WidgetUiActions}\nimport cards.nine.app.ui.launcher.{\n  EditWidgetsMode,\n  MoveTransformation,\n  NormalMode,\n  ResizeTransformation\n}\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.{AppWidgetType, MomentCategory, NineCardsMoment}\nimport cards.nine.models.{AppWidget, Widget, WidgetArea, WidgetData}\nimport cats.implicits._\nimport macroid.ActivityContextWrapper\n\nclass WidgetsJobs(\n    val widgetUiActions: WidgetUiActions,\n    val navigationUiActions: NavigationUiActions)(\n    implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs {\n\n  def showDialogForDeletingWidget(idWidget: Option[Int]): TaskService[Unit] =\n    idWidget match {\n      case Some(id) => navigationUiActions.deleteSelectedWidget(id)\n      case _        => navigationUiActions.showContactUsError()\n    }\n\n  def deleteWidget(id: Int): TaskService[Unit] =\n    for {\n      _ <- di.widgetsProcess.deleteWidget(id)\n      _ <- closeModeEditWidgets()\n      _ <- widgetUiActions.unhostWidget(id)\n    } yield ()\n\n  def loadWidgetsForMoment(nineCardsMoment: NineCardsMoment): TaskService[Unit] =\n    for {\n      _       <- widgetUiActions.clearWidgets()\n      moment  <- di.momentProcess.getMomentByType(nineCardsMoment)\n      widgets <- di.widgetsProcess.getWidgetsByMoment(moment.id)\n      _ <- widgets match {\n        case Nil => TaskService.empty\n        case w   => widgetUiActions.addWidgets(w)\n      }\n    } yield ()\n\n  def addWidget(maybeAppWidgetId: Option[Int]): TaskService[Unit] = {\n\n    def createWidget(appWidgetId: Int, nineCardsMoment: NineCardsMoment) =\n      for {\n        moment <- di.momentProcess.getMomentByType(nineCardsMoment)\n        widgetInfo <- widgetUiActions\n          .getWidgetInfoById(appWidgetId)\n          .resolveOption(s\"Widget information nor found with id $appWidgetId\")\n        (provider, cell) = widgetInfo\n        widgetsByMoment <- di.widgetsProcess.getWidgetsByMoment(moment.id)\n        space           <- getSpaceInTheScreen(widgetsByMoment, cell.spanX, cell.spanY)\n        widgetData = WidgetData(\n          momentId = moment.id,\n          packageName = provider.getPackageName,\n          className = provider.getClassName,\n          appWidgetId = Option(appWidgetId),\n          area = WidgetArea(\n            startX = space.startX,\n            startY = space.startY,\n            spanX = space.spanX,\n            spanY = space.spanY),\n          widgetType = AppWidgetType,\n          label = None,\n          imagePath = None,\n          intent = None)\n        widget <- di.widgetsProcess.addWidget(widgetData)\n      } yield widget\n\n    (for {\n      appWidgetId    <- maybeAppWidgetId\n      data           <- widgetUiActions.dom.getData.headOption\n      moment         <- data.moment\n      nineCardMoment <- moment.momentType\n    } yield {\n      val hostingWidgetId = statuses.hostingNoConfiguredWidget map (_.id)\n\n      hostingWidgetId match {\n        case Some(id) =>\n          statuses = statuses.copy(hostingNoConfiguredWidget = None)\n          for {\n            widget <- di.widgetsProcess.updateAppWidgetId(id, appWidgetId)\n            _      <- widgetUiActions.replaceWidget(widget)\n          } yield ()\n        case _ =>\n          for {\n            widget <- createWidget(appWidgetId, nineCardMoment)\n            _      <- di.trackEventProcess.addWidget(widget.packageName)\n            _      <- widgetUiActions.addWidgets(Seq(widget))\n          } yield ()\n      }\n    }) getOrElse navigationUiActions.showContactUsError()\n  }\n\n  def hostNoConfiguredWidget(widget: Widget): TaskService[Unit] = {\n    statuses = statuses.copy(hostingNoConfiguredWidget = Option(widget))\n    widgetUiActions.hostWidget(widget.packageName, widget.className)\n  }\n\n  def hostWidget(widget: AppWidget): TaskService[Unit] = {\n    statuses = statuses.copy(hostingNoConfiguredWidget = None)\n    val currentMomentType = widgetUiActions.dom.getData.headOption flatMap (_.moment) flatMap (_.momentType)\n    for {\n      _ <- currentMomentType match {\n        case Some(momentType) =>\n          di.trackEventProcess\n            .addWidgetToMoment(widget.packageName, widget.className, MomentCategory(momentType))\n        case _ => TaskService.empty\n      }\n      _ <- widgetUiActions.hostWidget(widget.packageName, widget.className)\n    } yield ()\n  }\n\n  def configureOrAddWidget(maybeAppWidgetId: Option[Int]): TaskService[Unit] =\n    maybeAppWidgetId match {\n      case Some(appWidgetId) => widgetUiActions.configureWidget(appWidgetId)\n      case _                 => navigationUiActions.showContactUsError()\n    }\n\n  def openModeEditWidgets(id: Int): TaskService[Unit] =\n    if (!widgetUiActions.dom.isWorkspaceScrolling) {\n      statuses =\n        statuses.copy(mode = EditWidgetsMode, transformation = None, idWidget = Option(id))\n      widgetUiActions.openModeEditWidgets()\n    } else {\n      TaskService.empty\n    }\n\n  def backToActionEditWidgets(): TaskService[Unit] = {\n    statuses = statuses.copy(transformation = None)\n    widgetUiActions.reloadViewEditWidgets()\n  }\n\n  def closeModeEditWidgets(): TaskService[Unit] =\n    for {\n      _ <- TaskService.right(statuses = statuses.copy(mode = NormalMode, idWidget = None))\n      _ <- di.widgetsProcess.updateWidgets(widgetUiActions.dom.getCurrentWidgets)\n      _ <- widgetUiActions.closeModeEditWidgets()\n    } yield ()\n\n  def cancelWidget(maybeAppWidgetId: Option[Int]): TaskService[Unit] =\n    (statuses.mode == EditWidgetsMode, maybeAppWidgetId) match {\n      case (true, Some(id)) => widgetUiActions.cancelWidget(id)\n      case _                => TaskService.empty\n    }\n\n  def editWidgetsShowActions(): TaskService[Unit] =\n    widgetUiActions.editWidgetsShowActions()\n\n  private[this] def getSpaceInTheScreen(\n      widgetsByMoment: Seq[Widget],\n      spanX: Int,\n      spanY: Int): TaskService[WidgetArea] = {\n\n    def searchSpace(widgets: Seq[Widget]): TaskService[WidgetArea] = {\n      val emptySpaces = (for {\n        column <- 0 to (WidgetsOps.columns - spanX)\n        row    <- 0 to (WidgetsOps.rows - spanY)\n      } yield {\n        val area = WidgetArea(startX = column, startY = row, spanX = spanX, spanY = spanY)\n        val hasConflict = widgets find (widget =>\n                                          widget.area.intersect(\n                                            area,\n                                            Option((WidgetsOps.rows, WidgetsOps.columns))))\n        if (hasConflict.isEmpty) Some(area) else None\n      }).flatten\n      emptySpaces.headOption match {\n        case Some(space) => TaskService.right(space)\n        case _           => TaskService.left(SpaceException(\"Widget don't have space\"))\n      }\n    }\n\n    for {\n      space <- searchSpace(widgetsByMoment)\n    } yield space\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/uiactions/AppDrawerUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs.uiactions\n\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.support.v7.widget.RecyclerView\nimport android.support.v7.widget.RecyclerView.LayoutManager\nimport android.view.{View, ViewGroup}\nimport android.widget.ImageView\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.adapters.apps.AppsAdapter\nimport cards.nine.app.ui.commons.adapters.contacts.{ContactsAdapter, LastCallsAdapter}\nimport cards.nine.app.ui.commons.adapters.search.SearchAdapter\nimport cards.nine.app.ui.commons.dialogs.wizard.{AppDrawerWizardInline, WizardInlinePreferences}\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.{SystemBarsTint, UiContext}\nimport cards.nine.app.ui.components.commons.SelectedItemDecoration\nimport cards.nine.app.ui.components.drawables.IconTypes\nimport cards.nine.app.ui.components.layouts._\nimport cards.nine.app.ui.components.layouts.snails.TabsSnails._\nimport cards.nine.app.ui.components.layouts.tweaks.FastScrollerLayoutTweak._\nimport cards.nine.app.ui.components.layouts.tweaks.PullToDownViewTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.PullToTabsViewTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.SearchBoxesViewTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.TabsViewTweaks._\nimport cards.nine.app.ui.components.widgets._\nimport cards.nine.app.ui.components.widgets.tweaks.DrawerRecyclerViewTweaks._\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.jobs.{AppDrawerJobs, DragJobs, NavigationJobs}\nimport cards.nine.app.ui.launcher.snails.DrawerSnails._\nimport cards.nine.app.ui.launcher.types.{AppDrawerIconShadowBuilder, _}\nimport cards.nine.app.ui.preferences.commons._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.types.theme._\nimport cards.nine.models.types.{GetAppOrder, GetByCategory, GetByInstallDate, GetByName}\nimport cards.nine.models.{\n  ApplicationData,\n  Contact,\n  IterableApplicationData,\n  IterableContacts,\n  LastCallsContact,\n  TermCounter,\n  _\n}\nimport cards.nine.process.device._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.DeviceVersion.Lollipop\nimport macroid.extras.DrawerLayoutTweaks._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.LinearLayoutTweaks._\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\n\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.Future\n\nclass AppDrawerUiActions(val dom: LauncherDOM)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_]) {\n\n  implicit lazy val systemBarsTint = new SystemBarsTint\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  lazy val wizardInlinePreferences = new WizardInlinePreferences()\n\n  lazy val appDrawerJobs: AppDrawerJobs = createAppDrawerJobs\n\n  lazy val navigationJobs: NavigationJobs = createNavigationJobs\n\n  lazy val dragJobs: DragJobs = createDragJobs\n\n  val pages = 2\n\n  val resistance = 2.4f\n\n  lazy val appDrawerTabs: Seq[TabInfo] = Seq(\n    TabInfo(R.drawable.app_drawer_filter_categories, resGetString(R.string.apps)),\n    TabInfo(R.drawable.app_drawer_filter_favorites, resGetString(R.string.contacts)))\n\n  lazy val appMenu = Seq(\n    (R.drawable.app_drawer_filter_alphabetical, resGetString(R.string.apps_alphabetical)),\n    (R.drawable.app_drawer_filter_categories, resGetString(R.string.apps_categories)),\n    (R.drawable.app_drawer_filter_installation_date, resGetString(R.string.apps_date)))\n\n  lazy val contactsMenu = Seq(\n    (R.drawable.app_drawer_filter_alphabetical, resGetString(R.string.contacts_alphabetical)),\n    (R.drawable.app_drawer_filter_favorites, resGetString(R.string.contacts_favorites)),\n    (R.drawable.app_drawer_filter_last_call, resGetString(R.string.contacts_last)))\n\n  def initialize(): TaskService[Unit] = {\n    val selectItemsInScrolling = AppDrawerSelectItemsInScroller.readValue\n    ((dom.searchBoxView <~\n      sbvUpdateContentView(AppsView) <~\n      sbvChangeListener(SearchBoxAnimatedListener(onHeaderIconClick = () => {\n        ((((dom.pullToTabsView ~> pdvIsEnabled()).get,\n           dom.isSearchingInGooglePlay,\n           dom.isDrawerTabsOpened) match {\n          case (_, true, _)     => backFromGooglePlaySearch()\n          case (false, _, _)    => Ui.nop\n          case (true, _, true)  => closeDrawerTabs()\n          case (true, _, false) => openTabs()\n        }) ~\n          (if (dom.isDrawerTabsOpened) hideMessageIfNecessary()\n           else showMessageIfNecessary())).run\n      }, onOptionsClick = () => {\n        val (icons, names) = dom.getTypeView match {\n          case Some(AppsView) => (appMenu.map(_._1), appMenu.map(_._2))\n          case Some(ContactView) =>\n            (contactsMenu.map(_._1), contactsMenu.map(_._2))\n          case _ => (Seq.empty, Seq.empty)\n        }\n        val width            = resGetDimensionPixelSize(R.dimen.width_popup_app_drawer)\n        val horizontalOffset = resGetDimensionPixelSize(R.dimen.size_icon_app_large) - width\n        (dom.searchBoxView.icon <~\n          vListThemedPopupWindowShow(\n            icons = icons,\n            values = names,\n            onItemClickListener = (position) => {\n              (((dom.getTypeView, position) match {\n                case (Some(AppsView), 0) =>\n                  loadAppsAndSaveStatus(AppsAlphabetical)\n                case (Some(AppsView), 1) =>\n                  loadAppsAndSaveStatus(AppsByCategories)\n                case (Some(AppsView), 2) =>\n                  loadAppsAndSaveStatus(AppsByLastInstall)\n                case (Some(ContactView), 0) =>\n                  loadContactsAndSaveStatus(ContactsAlphabetical)\n                case (Some(ContactView), 1) =>\n                  loadContactsAndSaveStatus(ContactsFavorites)\n                case (Some(ContactView), 2) =>\n                  loadContactsAndSaveStatus(ContactsByLastCall)\n                case _ => Ui.nop\n              }) ~\n                cleanFromGooglePlaySearch().ifUi(dom.isSearchingInGooglePlay) ~\n                (dom.searchBoxView <~ sbvClean)).run\n            },\n            width = Option(width),\n            horizontalOffset = Option(horizontalOffset))).run\n      })) <~\n      sbvOnChangeText((text: String) => {\n        (text, dom.getStatus, dom.getTypeView) match {\n          case (\"\", Some(status), Some(AppsView)) =>\n            AppsMenuOption(status) foreach (option =>\n                                              appDrawerJobs.loadApps(option).resolveAsync())\n          case (\"\", Some(status), Some(ContactView)) =>\n            ContactsMenuOption(status) foreach (option =>\n                                                  appDrawerJobs\n                                                    .loadContacts(option)\n                                                    .resolveAsyncServiceOr(manageException))\n          case (t, _, Some(AppsView)) =>\n            appDrawerJobs.loadAppsByKeyword(t).resolveAsync()\n          case (t, _, Some(ContactView)) =>\n            appDrawerJobs.loadContactsByKeyword(t).resolveAsyncServiceOr(manageException)\n          case _ =>\n        }\n        cleanFromGooglePlaySearch().ifUi(dom.isSearchingInGooglePlay).run\n      })) ~\n      (dom.tabs <~ tvClose) ~\n      (dom.drawerMessage <~ tvSizeResource(FontSize.getSizeResource) <~ tvColor(\n        theme.get(DrawerTextColor))) ~\n      (dom.appDrawerMain <~\n        appDrawerMainStyle <~\n        On.click(openDrawer(longClick = false)) <~\n        On.longClick(openDrawer(longClick = true) ~ Ui(true))) ~\n      (dom.recycler <~\n        recyclerStyle <~\n        (if (selectItemsInScrolling)\n           rvAddItemDecoration(new SelectedItemDecoration)\n         else Tweak.blank)) ~\n      (dom.scrollerLayout <~ scrollableStyle) ~\n      (dom.pullToTabsView <~\n        ptvLinkTabs(\n          tabs = Some(dom.tabs),\n          start = hideMessageIfNecessary,\n          end = showMessageIfNecessary) <~\n        ptvAddTabsAndActivate(appDrawerTabs, 0, None) <~\n        pdvResistance(resistance) <~\n        ptvListener(PullToTabsListener(\n          changeItem = (pos: Int) => {\n            ((pos match {\n              case 0 => loadAppsAlphabetical\n              case 1 => loadContactsAlphabetical\n              case _ => Ui.nop\n            }) ~ (if (dom.isDrawerTabsOpened) closeDrawerTabs()\n                  else Ui.nop) ~ (dom.searchBoxView <~ sbvClean)).run\n          }\n        ))) ~\n      loadAppsAlphabetical).toService()\n  }\n\n  def reloadAppsInDrawer(\n      apps: IterableApplicationData,\n      getAppOrder: GetAppOrder = GetByName,\n      counters: Seq[TermCounter] = Seq.empty): TaskService[Unit] =\n    if (apps.count() == 0) {\n      showSearchGooglePlayMessage().toService()\n    } else {\n      (hideMessage() ~\n        addApps(\n          apps = apps,\n          clickListener = (app: ApplicationData) =>\n            navigationJobs.openApp(app).resolveAsyncServiceOr(manageException),\n          longClickListener = (view: View, app: ApplicationData) => {\n            dragJobs.startAddItemToCollection(app).resolveAsync()\n            (view <~ vStartDrag(AddItemToCollection, new AppDrawerIconShadowBuilder(view))).run\n          },\n          getAppOrder = getAppOrder,\n          counters = counters)).toService()\n    }\n\n  def reloadContactsInDrawer(\n      contacts: IterableContacts,\n      counters: Seq[TermCounter] = Seq.empty): TaskService[Unit] =\n    if (contacts.count() == 0) {\n      showNoContactMessage().toService()\n    } else {\n      (hideMessage() ~\n        addContacts(\n          contacts = contacts,\n          clickListener = (contact: Contact) =>\n            navigationJobs.openContact(contact).resolveAsyncServiceOr(manageException),\n          longClickListener = (view: View, contact: Contact) => {\n            dragJobs.startAddItemToCollection(contact).resolveAsync()\n            (view <~ vStartDrag(AddItemToCollection, new AppDrawerIconShadowBuilder(view))).run\n          },\n          counters = counters)).toService()\n    }\n\n  def reloadSearchInDrawer(apps: Seq[NotCategorizedPackage]): TaskService[Unit] =\n    if (apps.isEmpty) {\n      showAppsNotFoundInGooglePlay().toService()\n    } else {\n      (hideMessage() ~\n        (dom.searchBoxView <~ vAddField(dom.searchingGooglePlayKey, true)) ~\n        addSearch(apps = apps, clickListener = (app: NotCategorizedPackage) => {\n          navigationJobs\n            .launchGooglePlay(app.packageName)\n            .resolveAsyncServiceOr(_ => navigationJobs.navigationUiActions.showContactUsError())\n        }) ~\n        (dom.searchBoxView <~ sbvUpdateHeaderIcon(IconTypes.BACK))).toService()\n    }\n\n  def reloadLastCallContactsInDrawer(contacts: Seq[LastCallsContact]): TaskService[Unit] =\n    if (contacts.isEmpty) {\n      showNoContactMessage().toService()\n    } else {\n      addLastCallContacts(\n        contacts,\n        (contact: LastCallsContact) =>\n          navigationJobs.openLastCall(contact.number).resolveAsyncServiceOr(manageException))\n        .toService()\n    }\n\n  def closeTabs(): TaskService[Unit] = closeDrawerTabs().toService()\n\n  def close(): TaskService[Unit] = {\n\n    def resetData() =\n      (if (dom.isEmptySearchBox && dom.isShowingAppsAlphabetical) {\n         (dom.recycler <~ rvScrollToTop) ~ (dom.scrollerLayout <~ fslReset)\n       } else {\n         (dom.recycler <~ rvCloseAdapter) ~\n           loadAppsAlphabetical ~\n           (dom.searchBoxView <~ sbvUpdateContentView(AppsView)) ~\n           (dom.pullToTabsView <~ ptvActivate(0))\n       }) ~ (dom.searchBoxView <~ sbvUpdateHeaderIcon(IconTypes.BURGER))\n\n    ((dom.searchBoxView <~ vAddField(dom.searchingGooglePlayKey, false)) ~\n      (dom.drawerLayout <~ dlUnlockedStart <~ (if (dom.hasCurrentMomentAssociatedCollection)\n                                                 dlUnlockedEnd\n                                               else Tweak.blank)) ~\n      (dom.topBarPanel <~ vVisible) ~\n      (dom.searchBoxView <~ sbvClean <~ sbvDisableSearch) ~\n      ((dom.drawerContent <~~\n        closeAppDrawer(AppDrawerAnimation.readValue, dom.appDrawerMain)) ~~\n        resetData())).toService()\n  }\n\n  def reloadContacts(): TaskService[Unit] = {\n    val option = dom.getStatus match {\n      case Some(status) => ContactsMenuOption(status)\n      case _            => None\n    }\n    loadContactsAndSaveStatus(option getOrElse ContactsAlphabetical).toService()\n  }\n\n  def reloadApps(): TaskService[Unit] = loadAppsAlphabetical.toService()\n\n  def showLoadingInGooglePlay(): TaskService[Unit] =\n    showSearchingInGooglePlay().toService()\n\n  private[this] def manageException(throwable: Throwable) = throwable match {\n    case e: CallPermissionException    => appDrawerJobs.requestReadCallLog()\n    case e: ContactPermissionException => appDrawerJobs.requestReadContacts()\n    case _                             => showGeneralError().toService()\n  }\n\n  private[this] def showSearchGooglePlayMessage(): Ui[Any] =\n    (dom.drawerMessage <~ tvText(R.string.apps_not_found) <~ vVisible) ~\n      (dom.searchBoxView <~ vAddField(dom.emptyInfoKey, true)) ~\n      (dom.recycler <~ vGone)\n\n  private[this] def showNoContactMessage(): Ui[Any] =\n    (dom.drawerMessage <~ tvText(R.string.contacts_not_found) <~ vVisible) ~\n      (dom.searchBoxView <~ vAddField(dom.emptyInfoKey, true)) ~\n      (dom.recycler <~ vGone)\n\n  private[this] def showSearchingInGooglePlay(): Ui[Any] =\n    (dom.drawerMessage <~ tvText(R.string.searching_in_google_play) <~ vVisible) ~\n      (dom.searchBoxView <~ vAddField(dom.emptyInfoKey, true)) ~\n      (dom.recycler <~ vGone)\n\n  private[this] def showAppsNotFoundInGooglePlay(): Ui[Any] =\n    (dom.drawerMessage <~ tvText(R.string.apps_not_found_in_google_play) <~ vVisible) ~\n      (dom.searchBoxView <~ vAddField(dom.emptyInfoKey, true)) ~\n      (dom.recycler <~ vGone)\n\n  private[this] def hideMessage(): Ui[Any] =\n    (dom.drawerMessage <~ vGone) ~\n      (dom.searchBoxView <~ vAddField(dom.emptyInfoKey, false)) ~\n      (dom.recycler <~ vVisible)\n\n  private[this] def hideMessageIfNecessary(): Ui[Any] =\n    (dom.drawerMessage <~ vInvisible).ifUi(dom.isShowingEmptyInfo)\n\n  private[this] def showMessageIfNecessary(): Ui[Any] =\n    (dom.drawerMessage <~ vVisible).ifUi(dom.isShowingEmptyInfo)\n\n  private[this] def showGeneralError(): Ui[Any] =\n    dom.workspaces <~ vLauncherSnackbar(R.string.contactUsError)\n\n  private[this] def openDrawer(longClick: Boolean) = {\n\n    def revealInDrawer(longClick: Boolean): Ui[Future[_]] = {\n      val showKeyboard = AppDrawerLongPressAction.readValue == AppDrawerLongPressActionOpenKeyboard && longClick\n      (dom.drawerLayout <~ dlLockedClosedStart <~ dlLockedClosedEnd) ~\n        ((dom.drawerContent <~~\n          openAppDrawer(AppDrawerAnimation.readValue, dom.appDrawerMain)) ~~\n          (dom.searchBoxView <~\n            sbvEnableSearch <~\n            (if (showKeyboard) sbvShowKeyboard else Tweak.blank)))\n    }\n\n    val loadContacts = AppDrawerLongPressAction.readValue == AppDrawerLongPressActionOpenContacts && longClick\n    (if (loadContacts) {\n       Ui(dom.recycler.getAdapter match {\n         case a: AppsAdapter => a.clear()\n         case _              =>\n       }) ~ loadContactsAlphabetical\n     } else if (dom.getItemsCount == 0) {\n       loadAppsAlphabetical\n     } else {\n       Ui.nop\n     }) ~ revealInDrawer(longClick) ~~ (dom.topBarPanel <~ vGone) ~ openAppDrawerWizardInline()\n  }\n\n  private[this] def openAppDrawerWizardInline(): Ui[Any] =\n    if (wizardInlinePreferences.shouldBeShowed(AppDrawerWizardInline)) {\n      dom.workspaces <~ vLauncherWizardSnackbar(AppDrawerWizardInline)\n    } else {\n      Ui.nop\n    }\n\n  private[this] def openTabs(): Ui[Any] =\n    (dom.tabs <~ tvOpen <~ showTabs) ~\n      (dom.recycler <~ hideList) ~\n      (dom.searchBoxView <~ sbvUpdateHeaderIcon(IconTypes.UP))\n\n  private[this] def closeDrawerTabs(): Ui[Any] =\n    (dom.tabs <~ tvClose <~ hideTabs) ~\n      (dom.recycler <~ showList) ~\n      (dom.searchBoxView <~ sbvUpdateHeaderIcon(IconTypes.BURGER))\n\n  private[this] def backFromGooglePlaySearch(): Ui[Any] =\n    loadAppsAlphabetical ~\n      (dom.searchBoxView <~ sbvUpdateHeaderIcon(IconTypes.BURGER) <~ sbvClean) ~\n      (dom.searchBoxView <~ vAddField(dom.searchingGooglePlayKey, false))\n\n  private[this] def cleanFromGooglePlaySearch(): Ui[Any] =\n    (dom.searchBoxView <~ sbvUpdateHeaderIcon(IconTypes.BURGER)) ~\n      (dom.searchBoxView <~ vAddField(dom.searchingGooglePlayKey, false))\n\n  private[this] def loadContactsAndSaveStatus(option: ContactsMenuOption): Ui[Any] = {\n    appDrawerJobs.loadContacts(option).resolveAsyncServiceOr(manageException)\n    dom.recycler <~ drvSetType(option)\n  }\n\n  private[this] def loadAppsAlphabetical: Ui[Any] = {\n    loadAppsAndSaveStatus(AppsAlphabetical) ~\n      (dom.searchBoxView <~ sbvUpdateContentView(AppsView))\n  }\n\n  private[this] def loadContactsAlphabetical: Ui[Any] = {\n    val favoriteContactsFirst = AppDrawerFavoriteContactsFirst.readValue\n    loadContactsAndSaveStatus(\n      if (favoriteContactsFirst) ContactsFavorites else ContactsAlphabetical) ~\n      (dom.searchBoxView <~ sbvUpdateContentView(ContactView))\n  }\n\n  private[this] def loadAppsAndSaveStatus(option: AppsMenuOption): Ui[Any] = {\n    appDrawerJobs.loadApps(option).resolveAsync()\n    dom.recycler <~ drvSetType(option)\n  }\n\n  private[this] def addApps(\n      apps: IterableApplicationData,\n      clickListener: (ApplicationData) => Unit,\n      longClickListener: (View, ApplicationData) => Unit,\n      getAppOrder: GetAppOrder = GetByName,\n      counters: Seq[TermCounter] = Seq.empty): Ui[Any] = {\n    val appsAdapter = AppsAdapter(\n      apps = apps,\n      clickListener = clickListener,\n      longClickListener = Option(longClickListener))\n    swipeAdapter(\n      adapter = appsAdapter,\n      layoutManager = appsAdapter.getLayoutManager,\n      counters = counters,\n      signalType = getAppOrder match {\n        case GetByInstallDate => FastScrollerInstallationDate\n        case GetByCategory    => FastScrollerCategory\n        case _                => FastScrollerText\n      })\n  }\n\n  private[this] def addContacts(\n      contacts: IterableContacts,\n      clickListener: (Contact) => Unit,\n      longClickListener: (View, Contact) => Unit,\n      counters: Seq[TermCounter] = Seq.empty): Ui[Any] = {\n    val contactAdapter = ContactsAdapter(\n      contacts = contacts,\n      clickListener = clickListener,\n      longClickListener = Some(longClickListener))\n    swipeAdapter(contactAdapter, contactAdapter.getLayoutManager, counters)\n  }\n\n  private[this] def addLastCallContacts(\n      contacts: Seq[LastCallsContact],\n      clickListener: (LastCallsContact) => Unit): Ui[Any] = {\n    val contactAdapter =\n      LastCallsAdapter(contacts = contacts, clickListener = clickListener)\n    swipeAdapter(contactAdapter, contactAdapter.getLayoutManager, Seq.empty)\n  }\n\n  private[this] def addSearch(\n      apps: Seq[NotCategorizedPackage],\n      clickListener: (NotCategorizedPackage) => Unit): Ui[Any] = {\n    val appsAdapter = new SearchAdapter(apps, clickListener)\n    swipeAdapter(\n      adapter = appsAdapter,\n      layoutManager = appsAdapter.getLayoutManager,\n      counters = Seq.empty)\n  }\n\n  private[this] def swipeAdapter(\n      adapter: RecyclerView.Adapter[_],\n      layoutManager: LayoutManager,\n      counters: Seq[TermCounter],\n      signalType: FastScrollerSignalType = FastScrollerText) = {\n    val addFieldTweaks = dom.getTypeView map {\n      case AppsView    => vAddField(SelectedItemDecoration.showLine, true)\n      case ContactView => vAddField(SelectedItemDecoration.showLine, false)\n    } getOrElse Tweak.blank\n    (dom.pullToTabsView <~ pdvEnable(true)) ~\n      (dom.recycler <~\n        rvCloseAdapter <~\n        vVisible <~\n        rvLayoutManager(layoutManager) <~\n        (if (dom.isEmptySearchBox)\n           rvLayoutAnimation(R.anim.list_slide_in_bottom_animation)\n         else Tweak.blank) <~\n        addFieldTweaks <~\n        rvAdapter(adapter) <~\n        rvScrollToTop) ~\n      scrollerLayoutUi(counters, signalType)\n  }\n\n  private[this] def scrollerLayoutUi(\n      counters: Seq[TermCounter],\n      signalType: FastScrollerSignalType): Ui[Any] =\n    dom.scrollerLayout <~\n      fslEnabledScroller(true) <~\n      fslLinkRecycler(dom.recycler) <~\n      fslReset <~\n      fslCounters(counters) <~\n      fslSignalType(signalType)\n\n  // Styles\n\n  def scrollableStyle(implicit context: ContextWrapper, theme: NineCardsTheme) = {\n    val padding = resGetDimensionPixelSize(R.dimen.padding_default)\n    vBackgroundBoxWorkspace(color = theme.get(DrawerBackgroundColor), horizontalPadding = padding) +\n      fslColor(theme.get(PrimaryColor), theme.get(DrawerTabsBackgroundColor)) +\n      fslMarginRightBarContent(padding)\n  }\n\n  def appDrawerMainStyle(\n      implicit context: ContextWrapper,\n      theme: NineCardsTheme): Tweak[TintableImageView] = {\n    val elevation = resGetDimensionPixelSize(R.dimen.elevation_pressed)\n    Lollipop ifSupportedThen {\n      vStateListAnimator(R.anim.elevation_transition) +\n        vPaddings(elevation) +\n        vCircleOutlineProvider(elevation)\n    } getOrElse tivPressedColor(theme.get(DockPressedColor))\n  }\n\n  def recyclerStyle(implicit context: ContextWrapper, theme: NineCardsTheme): Tweak[RecyclerView] =\n    rvFixedSize\n\n  def paginationDrawerItemStyle(implicit context: ContextWrapper): Tweak[ImageView] = {\n    val margin = resGetDimensionPixelSize(R.dimen.margin_pager_drawer)\n    val size   = resGetDimensionPixelSize(R.dimen.drawer_size_pager)\n    lp[ViewGroup](size, size) +\n      llLayoutMargin(margin, margin, margin, margin) +\n      ivSrc(R.drawable.drawer_pager)\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/uiactions/DockAppsUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs.uiactions\n\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.layouts.tweaks.DockAppsPanelLayoutTweaks._\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.DockAppData\nimport cards.nine.models.NineCardsTheme\nimport macroid.{ActivityContextWrapper, FragmentManagerContext}\n\nclass DockAppsUiActions(val dom: LauncherDOM)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_]) {\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  def loadDockApps(apps: Seq[DockAppData]): TaskService[Unit] =\n    (dom.dockAppsPanel <~ daplInit(apps)).toService(Option(\"loadDockApps\"))\n\n  def reloadDockApps(dockApp: DockAppData): TaskService[Unit] =\n    (dom.dockAppsPanel <~ daplReload(dockApp)).toService()\n\n  def reset(): TaskService[Unit] =\n    (dom.dockAppsPanel <~ daplReset()).toService()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/uiactions/DragUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs.uiactions\n\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.Constants._\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.{AppUtils, UiContext}\nimport cards.nine.app.ui.components.layouts._\nimport cards.nine.app.ui.components.layouts.tweaks.CollectionActionsPanelLayoutTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.LauncherWorkSpacesTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.TopBarLayoutTweaks._\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.types.{AppCardType, CardType}\nimport cards.nine.models.NineCardsTheme\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\n\nimport scala.concurrent.ExecutionContext.Implicits.global\n\nclass DragUiActions(val dom: LauncherDOM)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_]) {\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  lazy val actionForCollections = Seq(\n    CollectionActionItem(\n      resGetString(R.string.edit),\n      R.drawable.icon_launcher_action_edit,\n      CollectionActionEdit),\n    CollectionActionItem(\n      resGetString(R.string.remove),\n      R.drawable.icon_launcher_action_remove,\n      CollectionActionRemove))\n\n  lazy val actionForApps = Seq(\n    CollectionActionItem(\n      resGetString(R.string.appInfo),\n      R.drawable.icon_launcher_action_info_app,\n      CollectionActionAppInfo),\n    CollectionActionItem(\n      resGetString(R.string.uninstall),\n      R.drawable.icon_launcher_action_uninstall,\n      CollectionActionUninstall))\n\n  lazy val actionForAppsFromDockApps = Seq(\n    CollectionActionItem(\n      resGetString(R.string.remove),\n      R.drawable.icon_launcher_action_remove,\n      CollectionActionRemoveDockApp),\n    CollectionActionItem(\n      resGetString(R.string.uninstall),\n      R.drawable.icon_launcher_action_uninstall,\n      CollectionActionUninstall))\n\n  def startAddItem(cardType: CardType): TaskService[Unit] = {\n    ((dom.topBarPanel <~ applyFadeOut()) ~\n      (cardType match {\n        case AppCardType =>\n          dom.collectionActionsPanel <~ caplLoad(actionForApps) <~ applyFadeIn()\n        case _ => Ui.nop\n      }) ~\n      reloadEdges()).toService()\n  }\n\n  def startAddItemFromDockApp(cardType: CardType): TaskService[Unit] = {\n    ((dom.topBarPanel <~ applyFadeOut()) ~\n      (cardType match {\n        case AppCardType =>\n          dom.collectionActionsPanel <~ caplLoad(actionForAppsFromDockApps) <~ applyFadeIn()\n        case _ => Ui.nop\n      }) ~\n      reloadEdges()).toService()\n  }\n\n  def endAddItem(): TaskService[Unit] =\n    ((dom.topBarPanel <~ applyFadeIn()) ~\n      (dom.collectionActionsPanel <~~ applyFadeOut()) ~\n      hideEdges()).toService()\n\n  def startReorder(): TaskService[Unit] =\n    ((dom.dockAppsPanel <~ applyFadeOut()) ~\n      (dom.topBarPanel <~ applyFadeOut()) ~\n      (dom.collectionActionsPanel <~ caplLoad(actionForCollections) <~ applyFadeIn()) ~\n      reloadEdges()).toService()\n\n  def endReorder(): TaskService[Unit] =\n    ((dom.dockAppsPanel <~ applyFadeIn()) ~\n      (dom.topBarPanel <~ applyFadeIn()) ~\n      (dom.collectionActionsPanel <~~ applyFadeOut()) ~\n      hideEdges()).toService()\n\n  def goToNextScreenReordering(): TaskService[Unit] = {\n    val canMoveToNextScreen =\n      (dom.workspaces ~> lwsCanMoveToNextScreenOnlyCollections()).get\n    (goToNextWorkspace() ~\n      (dom.workspaces <~ lwsPrepareItemsScreenInReorder(0)) ~\n      reloadEdges()).ifUi(canMoveToNextScreen).toService()\n  }\n\n  def goToPreviousScreenReordering(): TaskService[Unit] = {\n    val canMoveToPreviousScreen =\n      (dom.workspaces ~> lwsCanMoveToPreviousScreenOnlyCollections()).get\n    (goToPreviousWorkspace() ~\n      (dom.workspaces <~ lwsPrepareItemsScreenInReorder(numSpaces - 1)) ~\n      reloadEdges()).ifUi(canMoveToPreviousScreen).toService()\n  }\n\n  def goToPreviousScreenAddingItem(): TaskService[Unit] = {\n    val canMoveToPreviousScreen =\n      (dom.workspaces ~> lwsCanMoveToPreviousScreen()).get\n    (goToPreviousWorkspace() ~ reloadEdges()).ifUi(canMoveToPreviousScreen).toService()\n  }\n\n  def goToNextScreenAddingItem(): TaskService[Unit] = {\n    val canMoveToNextScreen = (dom.workspaces ~> lwsCanMoveToNextScreen()).get\n    (goToNextWorkspace() ~ reloadEdges()).ifUi(canMoveToNextScreen).toService()\n  }\n\n  private[this] def goToNextWorkspace(): Ui[Any] =\n    (dom.workspaces ~> lwsNextScreen()).get map { next =>\n      goToWorkspace(next)\n    } getOrElse Ui.nop\n\n  private[this] def goToPreviousWorkspace(): Ui[Any] =\n    (dom.workspaces ~> lwsPreviousScreen()).get map { previous =>\n      goToWorkspace(previous)\n    } getOrElse Ui.nop\n\n  private[this] def goToWorkspace(page: Int): Ui[Any] =\n    (dom.getData.lift(page) map (data =>\n                                   dom.topBarPanel <~ tblReloadByType(data.workSpaceType)) getOrElse Ui.nop) ~\n      (dom.workspaces <~ lwsSelect(page)) ~\n      (dom.paginationPanel <~ ivReloadPager(page))\n\n  private[this] def reloadEdges(): Ui[Any] = {\n    val canMoveToNextScreen =\n      (dom.workspaces ~> lwsCanMoveToNextScreenOnlyCollections()).get\n    val canMoveToPreviousScreen =\n      (dom.workspaces ~> lwsCanMoveToPreviousScreenOnlyCollections()).get\n    (dom.workspacesEdgeLeft <~ (if (canMoveToPreviousScreen) vVisible\n                                else vGone)) ~\n      (dom.workspacesEdgeRight <~ (if (canMoveToNextScreen) vVisible\n                                   else vGone))\n  }\n\n  private[this] def hideEdges(): Ui[Any] =\n    (dom.workspacesEdgeLeft <~ vGone) ~\n      (dom.workspacesEdgeRight <~ vGone)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/uiactions/LauncherDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs.uiactions\n\nimport android.app.Activity\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.support.v4.view.GravityCompat\nimport android.view.View\nimport cards.nine.app.ui.commons.ActivityFindViews\nimport cards.nine.app.ui.commons.dialogs.BaseActionFragment\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.layouts.tweaks.LauncherWorkSpacesTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.TabsViewTweaks._\nimport cards.nine.app.ui.components.models.LauncherData\nimport cards.nine.app.ui.components.widgets.{AppsView, ContentView}\nimport cards.nine.app.ui.launcher.types.AppsAlphabetical\nimport cards.nine.models.{Collection, Widget}\nimport cards.nine.models.types.NineCardsMoment\nimport com.fortysevendeg.ninecardslauncher.TR\nimport macroid._\nimport macroid.extras.FragmentExtras._\n\nclass LauncherDOM(activity: Activity) {\n\n  import ActivityFindViews._\n\n  val nameActionFragment = \"action-fragment\"\n\n  val searchingGooglePlayKey = \"searching-google-play-key\"\n\n  val emptyInfoKey = \"empty-info-key\"\n\n  lazy val foreground = findView(TR.launcher_foreground).run(activity)\n\n  lazy val appsMoment = findView(TR.launcher_apps_moment).run(activity)\n\n  lazy val drawerLayout = findView(TR.launcher_drawer_layout).run(activity)\n\n  lazy val navigationView = findView(TR.launcher_navigation_view).run(activity)\n\n  lazy val menuName = findView(TR.menu_name).run(activity)\n\n  lazy val menuEmail = findView(TR.menu_email).run(activity)\n\n  lazy val menuAvatar = findView(TR.menu_avatar).run(activity)\n\n  lazy val menuHeader = findView(TR.menu_header).run(activity)\n\n  lazy val menuCover = findView(TR.menu_cover).run(activity)\n\n  lazy val loading = findView(TR.launcher_loading).run(activity)\n\n  lazy val root = findView(TR.launcher_root).run(activity)\n\n  lazy val dockAppsPanel = findView(TR.launcher_dock_apps_panel).run(activity)\n\n  lazy val content = findView(TR.launcher_content).run(activity)\n\n  lazy val workspaces = findView(TR.launcher_work_spaces).run(activity)\n\n  lazy val workspacesEdgeLeft =\n    findView(TR.launcher_work_spaces_edge_left).run(activity)\n\n  lazy val workspacesEdgeRight =\n    findView(TR.launcher_work_spaces_edge_right).run(activity)\n\n  lazy val paginationPanel =\n    findView(TR.launcher_pagination_panel).run(activity)\n\n  lazy val topBarPanel = findView(TR.launcher_top_bar_panel).run(activity)\n\n  lazy val collectionActionsPanel =\n    findView(TR.launcher_collections_actions_panel).run(activity)\n\n  lazy val menuCollectionRoot = findView(TR.menu_collection_root).run(activity)\n\n  lazy val menuWorkspaceContent =\n    findView(TR.menu_workspace_content).run(activity)\n\n  lazy val menuLauncherContent =\n    findView(TR.menu_launcher_content).run(activity)\n\n  lazy val menuLauncherWallpaper =\n    findView(TR.menu_launcher_wallpaper).run(activity)\n\n  lazy val menuLauncherWidgets =\n    findView(TR.menu_launcher_widgets).run(activity)\n\n  lazy val menuLauncherSettings =\n    findView(TR.menu_launcher_settings).run(activity)\n\n  lazy val appDrawerMain = findView(TR.launcher_app_drawer).run(activity)\n\n  lazy val drawerContent = findView(TR.launcher_drawer_content).run(activity)\n\n  lazy val drawerMessage = findView(TR.launcher_drawer_message).run(activity)\n\n  lazy val scrollerLayout =\n    findView(TR.launcher_drawer_scroller_layout).run(activity)\n\n  lazy val recycler = findView(TR.launcher_drawer_recycler).run(activity)\n\n  lazy val tabs = findView(TR.launcher_drawer_tabs).run(activity)\n\n  lazy val pullToTabsView =\n    findView(TR.launcher_drawer_pull_to_tabs).run(activity)\n\n  lazy val searchBoxView =\n    findView(TR.launcher_search_box_content).run(activity)\n\n  def getWorksSpacesCount: Int = workspaces.getWorksSpacesCount\n\n  def getData: Seq[LauncherData] = workspaces.data\n\n  def getCurrentWidgets: Seq[Widget] = workspaces.getWidgets\n\n  def hasCurrentMomentAssociatedCollection: Boolean =\n    (getData.headOption flatMap (_.moment) flatMap (_.collection)).isDefined\n\n  def getCurrentMomentType: Option[NineCardsMoment] =\n    getData.headOption flatMap (_.moment) flatMap (_.momentType)\n\n  def getCurrentMomentTypeName: Option[String] =\n    getCurrentMomentType map (_.name)\n\n  def isMenuVisible: Boolean = drawerLayout.isDrawerOpen(GravityCompat.START)\n\n  def isAppsByMomentMenuVisible: Boolean =\n    drawerLayout.isDrawerOpen(GravityCompat.END)\n\n  def isBackgroundMenuVisible: Boolean =\n    workspaces.workSpacesStatuses.openedMenu\n\n  def isDrawerTabsOpened: Boolean = (tabs ~> isOpened).get\n\n  def getStatus: Option[String] = recycler.getType\n\n  def getTypeView: Option[ContentView] = Option(recycler.statuses.contentView)\n\n  def isDrawerShowingApps: Boolean = getTypeView.contains(AppsView)\n\n  def getItemsCount: Int =\n    Option(recycler.getAdapter) map (_.getItemCount) getOrElse 0\n\n  def getDrawerWidth: Int = drawerContent.getWidth\n\n  def isDrawerVisible: Boolean = drawerContent.getVisibility == View.VISIBLE\n\n  def isSearchingInGooglePlay: Boolean =\n    searchBoxView.getField[Boolean](searchingGooglePlayKey) getOrElse false\n\n  def isShowingEmptyInfo: Boolean =\n    searchBoxView.getField[Boolean](emptyInfoKey) getOrElse false\n\n  def isEmptyCollections: Boolean = (workspaces ~> lwsEmptyCollections).get\n\n  def isEmptySearchBox: Boolean = searchBoxView.isEmpty\n\n  def isShowingAppsAlphabetical: Boolean =\n    recycler.isType(AppsAlphabetical.name)\n\n  def isCollectionWorkspace: Boolean =\n    (workspaces ~> lwsIsCollectionWorkspace).get\n\n  def isWorkspaceScrolling: Boolean =\n    workspaces.animatedWorkspaceStatuses.isScrolling\n\n  def getCollections: Seq[Collection] = (workspaces ~> lwsGetCollections()).get\n\n  def getCollection(position: Int): Option[Collection] =\n    getCollections.lift(position)\n\n  def getCountCollections: Int = (workspaces ~> lwsCountCollections).get\n\n  def canRemoveCollections: Boolean = getCountCollections > 1\n\n  def isActionShowed(implicit fragmentManagerContext: FragmentManagerContext[\n    Fragment,\n    FragmentManager]): Boolean = findFragmentByTag(nameActionFragment).isDefined\n\n  def getFragment(\n      implicit fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager]): Option[\n    BaseActionFragment] =\n    findFragmentByTag[BaseActionFragment](nameActionFragment)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/uiactions/LauncherUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs.uiactions\n\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.support.v7.app.AppCompatActivity\nimport android.view.DragEvent._\nimport android.view.View.OnDragListener\nimport android.view.{DragEvent, View, WindowManager}\nimport cards.nine.app.ui.commons.CommonsExcerpt._\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.components.layouts.tweaks.AppsMomentLayoutTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.CollectionActionsPanelLayoutTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.DockAppsPanelLayoutTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.LauncherWorkSpacesTweaks._\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.launcher.types.{\n  AddItemToCollection,\n  DragObject,\n  ReorderCollection,\n  ReorderWidget\n}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.NineCardsTheme\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\nimport macroid.extras.DeviceVersion.KitKat\nimport macroid.extras.ViewTweaks._\n\nclass LauncherUiActions(val dom: LauncherDOM)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_]) {\n\n  implicit lazy val systemBarsTint = new SystemBarsTint\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  val widgetsJobs = createWidgetsJobs\n\n  def initialize(): TaskService[Unit] =\n    (systemBarsTint.initAllSystemBarsTint() ~\n      prepareBars ~\n      (dom.root <~ dragListener())).toService()\n\n  def resetFromCollection(): TaskService[Unit] =\n    (dom.foreground <~ vBlankBackground <~ vGone).toService()\n\n  def reloadAllViews(): TaskService[Unit] =\n    activityContextWrapper.original.get match {\n      case Some(activity: AppCompatActivity) =>\n        TaskService.right(activity.recreate())\n      case _ => TaskService.empty\n    }\n\n  private[this] def prepareBars =\n    KitKat.ifSupportedThen {\n      val activity = activityContextWrapper.getOriginal\n      val sbHeight = systemBarsTint.getStatusBarHeight\n      val nbHeight = systemBarsTint.getNavigationBarHeight\n      Ui(\n        activity.getWindow.setFlags(\n          WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,\n          WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)) ~\n        (dom.content <~ vPadding(0, sbHeight, 0, nbHeight)) ~\n        (dom.menuCollectionRoot <~ vPadding(0, sbHeight, 0, nbHeight)) ~\n        (dom.drawerContent <~ vPadding(0, sbHeight, 0, nbHeight)) ~\n        (dom.appsMoment <~ amlPaddingTopAndBottom(sbHeight, nbHeight)) ~\n        (dom.drawerLayout <~ vBackground(R.drawable.background_workspace))\n    } getOrElse Ui.nop\n\n  private[this] def dragListener(): Tweak[View] = Tweak[View] { view =>\n    view.setOnDragListener(\n      new OnDragListener {\n        val dragAreaKey = \"drag-area\"\n\n        override def onDrag(v: View, event: DragEvent): Boolean = {\n          val dragArea = v.getField[DragArea](dragAreaKey) getOrElse NoDragArea\n          (event.getAction, (dom.topBarPanel ~> height).get, (dom.dockAppsPanel ~> height).get) match {\n            case (_, topBar, bottomBar) =>\n              val height = KitKat.ifSupportedThen(\n                  view.getHeight - systemBarsTint.getStatusBarHeight) getOrElse view.getHeight\n              val top = KitKat\n                  .ifSupportedThen(topBar + systemBarsTint.getStatusBarHeight) getOrElse topBar\n              // Project location to views\n              val x = event.getX\n              val y = event.getY\n              val currentDragArea =\n                if (y < top) ActionsDragArea\n                else if (y > height - bottomBar) DockAppsDragArea\n                else WorkspacesDragArea\n\n              val (action, area) = if (dragArea != currentDragArea) {\n                (v <~ vAddField(dragAreaKey, currentDragArea)).run\n                (ACTION_DRAG_EXITED, dragArea)\n              } else {\n                (event.getAction, currentDragArea)\n              }\n\n              (area, event.getLocalState, action) match {\n                case (WorkspacesDragArea, DragObject(_, ReorderWidget), _) =>\n                  // Project to workspace\n                  (dom.workspaces <~ lwsDragReorderWidgetDispatcher(action, x, y - top)).run\n                case (WorkspacesDragArea, DragObject(_, AddItemToCollection), _) =>\n                  // Project to workspace\n                  (dom.workspaces <~ lwsDragAddItemDispatcher(action, x, y - top)).run\n                case (DockAppsDragArea, DragObject(_, AddItemToCollection), _) =>\n                  // Project to dock apps\n                  (dom.dockAppsPanel <~ daplDragDispatcher(action, x, y - (height - bottomBar))).run\n                case (WorkspacesDragArea, DragObject(_, ReorderCollection), _) =>\n                  // Project to workspace\n                  (dom.workspaces <~ lwsDragReorderCollectionDispatcher(action, x, y - top)).run\n                case (DockAppsDragArea, DragObject(_, ReorderCollection), ACTION_DROP) =>\n                  // Project to dock apps\n                  (dom.workspaces <~ lwsDragReorderCollectionDispatcher(action, x, y - top)).run\n                case (ActionsDragArea, DragObject(_, ReorderCollection), ACTION_DROP) =>\n                  // Project to Collection actions\n                  ((dom.collectionActionsPanel <~ caplDragDispatcher(action, x, y)) ~\n                    (dom.workspaces <~ lwsDragReorderCollectionDispatcher(action, x, y - top))).run\n                case (ActionsDragArea, DragObject(_, ReorderWidget), ACTION_DROP) =>\n                  // Project to Collection actions\n                  ((dom.collectionActionsPanel <~ caplDragDispatcher(action, x, y)) ~\n                    Ui(widgetsJobs.closeModeEditWidgets().resolveAsync())).run\n                case (ActionsDragArea, _, _) =>\n                  // Project to Collection actions\n                  (dom.collectionActionsPanel <~ caplDragDispatcher(action, x, y)).run\n                case _ =>\n              }\n            case _ =>\n          }\n          true\n        }\n      })\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/uiactions/MenuDrawersUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs.uiactions\n\nimport android.content.res.ColorStateList\nimport android.support.design.widget.NavigationView\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.widget.ImageView\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.CommonsExcerpt._\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.drawables.CharDrawable\nimport cards.nine.app.ui.components.layouts.tweaks.AppsMomentLayoutTweaks._\nimport cards.nine.app.ui.components.models.LauncherMoment\nimport cards.nine.app.ui.components.layouts.tweaks.LauncherWorkSpacesTweaks._\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.{DrawerBackgroundColor, DrawerIconColor, DrawerTextColor}\nimport macroid.extras.DeviceVersion.Lollipop\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.NavigationViewTweaks._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport macroid.extras.DrawerLayoutTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\nimport macroid.FullDsl._\n\nclass MenuDrawersUiActions(val dom: LauncherDOM)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_]) {\n\n  implicit lazy val systemBarsTint = new SystemBarsTint\n\n  lazy val navigationJobs = createNavigationJobs\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  def initialize(): TaskService[Unit] = {\n    ((dom.drawerLayout <~ dlStatusBarBackground(R.color.primary)) ~\n      (dom.navigationView <~\n        navigationViewStyle <~\n        nvNavigationItemSelectedListener(itemId => {\n          navigationJobs.goToMenuOption(itemId).resolveAsync()\n          closeMenu().run\n          true\n        }))).toService()\n  }\n\n  def loadUserProfileMenu(\n      maybeEmail: Option[String],\n      maybeName: Option[String],\n      maybeAvatarUrl: Option[String],\n      maybeCoverUrl: Option[String]): TaskService[Unit] =\n    ((dom.menuName <~ tvText(maybeName.getOrElse(\"\"))) ~\n      (dom.menuEmail <~ tvText(maybeEmail.getOrElse(\"\"))) ~\n      (dom.menuAvatar <~\n        ((maybeAvatarUrl, maybeName) match {\n          case (Some(url), _) => ivUri(url)\n          case (_, Some(name)) =>\n            ivSrc(CharDrawable(name.substring(0, 1).toUpperCase))\n          case _ => ivBlank\n        }) <~\n        menuAvatarStyle) ~\n      (dom.menuHeader <~ On.click {\n        closeMenu() ~ Ui(navigationJobs.navigationUiActions.goToProfile().resolveAsync())\n      }) ~\n      (dom.menuCover <~\n        (maybeCoverUrl match {\n          case Some(url) => ivUri(url)\n          case None      => ivBlank\n        }))).toService(Option(\"loadUserProfileMenu\"))\n\n  def openMenu(): TaskService[Unit] =\n    (dom.drawerLayout <~ dlOpenDrawer).toService()\n\n  def reloadBarMoment(data: LauncherMoment): TaskService[Unit] =\n    ((dom.workspaces <~ lwsReloadMomentCollection(data.collection)) ~\n      (dom.appsMoment <~ amlPopulate(data)) ~\n      (dom.drawerLayout <~ (data.collection match {\n        case Some(_) => dlUnlockedEnd\n        case None    => dlLockedClosedEnd\n      }))).toService(Option(\"reloadBarMoment\"))\n\n  def openAppsMoment(): TaskService[Unit] =\n    (if ((dom.drawerLayout ~> dlIsLockedClosedDrawerEnd).get) {\n       Ui.nop\n     } else {\n       dom.drawerLayout <~ dlOpenDrawerEnd\n     }).toService()\n\n  def closeAppsMoment(): TaskService[Unit] =\n    (dom.drawerLayout <~ dlCloseDrawerEnd).toService()\n\n  def close(): TaskService[Unit] = closeMenu().toService()\n\n  private[this] def closeMenu(): Ui[Any] = dom.drawerLayout <~ dlCloseDrawer\n\n  // Styles\n\n  private[this] def navigationViewStyle(\n      implicit context: ContextWrapper,\n      theme: NineCardsTheme): Tweak[NavigationView] =\n    Tweak[NavigationView] { view =>\n      view.setBackgroundColor(theme.get(DrawerBackgroundColor))\n      view.setItemTextColor(ColorStateList.valueOf(theme.get(DrawerTextColor)))\n      view.setItemIconTintList(ColorStateList.valueOf(theme.get(DrawerIconColor)))\n    }\n\n  private[this] def menuAvatarStyle(implicit context: ContextWrapper): Tweak[ImageView] =\n    Lollipop ifSupportedThen {\n      vCircleOutlineProvider()\n    } getOrElse Tweak.blank\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/uiactions/NavigationUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs.uiactions\n\nimport android.content.Intent\nimport android.graphics.Point\nimport android.os.Bundle\nimport android.support.design.widget.Snackbar\nimport android.support.v4.app.{DialogFragment, Fragment, FragmentManager}\nimport cards.nine.app.ui.collections.CollectionsDetailsActivity\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.RequestCodes._\nimport cards.nine.app.ui.commons.SafeUi._\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.dialogs.{AlertDialogFragment, MomentDialog}\nimport cards.nine.app.ui.components.drawables.RippleCollectionDrawable\nimport cards.nine.app.ui.components.layouts.tweaks.LauncherWorkSpacesTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.TopBarLayoutTweaks._\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.commons.dialogs.addmoment.AddMomentFragment\nimport cards.nine.app.ui.commons.dialogs.createoreditcollection.CreateOrEditCollectionFragment\nimport cards.nine.app.ui.commons.dialogs.editmoment.EditMomentFragment\nimport cards.nine.app.ui.commons.dialogs.privatecollections.PrivateCollectionsFragment\nimport cards.nine.app.ui.commons.dialogs.publicollections.PublicCollectionsFragment\nimport cards.nine.app.ui.commons.dialogs.widgets.WidgetsFragment\nimport cards.nine.app.ui.launcher.jobs.LauncherJobs\nimport cards.nine.app.ui.preferences.NineCardsPreferencesActivity\nimport cards.nine.app.ui.preferences.commons.{\n  CircleOpeningCollectionAnimation,\n  CollectionOpeningAnimations\n}\nimport cards.nine.app.ui.profile.ProfileActivity\nimport cards.nine.app.ui.wizard.WizardActivity\nimport cards.nine.commons._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.theme.CardLayoutBackgroundColor\nimport cards.nine.models.{Collection, Moment, NineCardsTheme}\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\nimport macroid.extras.DeviceVersion.KitKat\nimport macroid.extras.FragmentExtras._\nimport macroid.extras.ViewTweaks._\n\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.Future\n\nclass NavigationUiActions(val dom: LauncherDOM)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_])\n    extends ImplicitsUiExceptions {\n\n  implicit lazy val systemBarsTint = new SystemBarsTint\n\n  implicit lazy val launcherJobs: LauncherJobs = createLauncherJobs\n\n  implicit lazy val widgetsJobs = createWidgetsJobs\n\n  implicit lazy val navigationJobs = createNavigationJobs\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  val pageMoments = 0\n\n  val pageCollections = 1\n\n  val maxBackgroundPercent: Float = 0.7f\n\n  val tagDialog = \"dialog\"\n\n  def goToWizard(): TaskService[Unit] =\n    uiStartIntent(new Intent(activityContextWrapper.bestAvailable, classOf[WizardActivity]))\n      .toService()\n\n  def goToCollection(collection: Collection, point: Point): TaskService[Unit] = {\n    def rippleToCollection: Ui[Future[Any]] = {\n      val color      = theme.getIndexColor(collection.themedColorIndex)\n      val y          = KitKat.ifSupportedThen(point.y - systemBarsTint.getStatusBarHeight) getOrElse point.y\n      val background = new RippleCollectionDrawable(point.x, y, color)\n      (dom.foreground <~\n        vVisible <~\n        vBackground(background)) ~\n        background.start()\n    }\n\n    val intent =\n      new Intent(activityContextWrapper.bestAvailable, classOf[CollectionsDetailsActivity])\n    intent.putExtra(CollectionsDetailsActivity.startPositionKey, collection.position)\n    intent.putExtra(\n      CollectionsDetailsActivity.toolbarColorKey,\n      theme.getIndexColor(collection.themedColorIndex))\n    intent.putExtra(\n      CollectionsDetailsActivity.backgroundColorKey,\n      theme.get(CardLayoutBackgroundColor))\n    intent.putExtra(CollectionsDetailsActivity.toolbarIconKey, collection.icon)\n    CollectionOpeningAnimations.readValue match {\n      case anim @ CircleOpeningCollectionAnimation if anim.isSupported =>\n        (rippleToCollection ~~ uiStartIntentForResult(intent, RequestCodes.goToCollectionDetails))\n          .toService()\n      case _ => uiStartIntent(intent).toService()\n    }\n  }\n\n  def launchCreateOrCollection(bundle: Bundle): TaskService[Unit] =\n    showAction(new CreateOrEditCollectionFragment, bundle).toService()\n\n  def launchPrivateCollection(bundle: Bundle): TaskService[Unit] =\n    showAction(new PrivateCollectionsFragment, bundle).toService()\n\n  def launchPublicCollection(bundle: Bundle): TaskService[Unit] =\n    showAction(new PublicCollectionsFragment, bundle).toService()\n\n  def launchAddMoment(bundle: Bundle): TaskService[Unit] =\n    showAction(new AddMomentFragment, bundle).toService()\n\n  def launchEditMoment(bundle: Bundle): TaskService[Unit] =\n    showAction(new EditMomentFragment, bundle).toService()\n\n  def launchWidgets(bundle: Bundle): TaskService[Unit] =\n    showAction(new WidgetsFragment, bundle).toService()\n\n  def launchSettings(): TaskService[Unit] =\n    uiStartIntentForResult(\n      intent =\n        new Intent(activityContextWrapper.getOriginal, classOf[NineCardsPreferencesActivity]),\n      requestCode = goToPreferences).toService()\n\n  def launchWallpaper(): TaskService[Unit] =\n    uiStartIntent(new Intent(Intent.ACTION_SET_WALLPAPER)).toService()\n\n  def deleteSelectedWidget(id: Int): TaskService[Unit] =\n    Ui {\n      val ft = fragmentManagerContext.manager.beginTransaction()\n      Option(fragmentManagerContext.manager.findFragmentByTag(tagDialog)) foreach ft.remove\n      ft.addToBackStack(javaNull)\n      val dialog = new AlertDialogFragment(\n        message = R.string.removeWidgetMessage,\n        positiveAction = () =>\n          widgetsJobs\n            .deleteWidget(id)\n            .resolveAsyncServiceOr(_ => widgetsJobs.navigationUiActions.showContactUsError()))\n      dialog.show(ft, tagDialog)\n    }.toService()\n\n  def showSelectMomentDialog(moments: Seq[Moment]): TaskService[Unit] =\n    Ui {\n      val momentDialog = new MomentDialog(moments)\n      momentDialog.show(fragmentManagerContext.manager, tagDialog)\n    }.toService()\n\n  def showDialogForRemoveCollection(collection: Collection): TaskService[Unit] =\n    Ui {\n      val ft = fragmentManagerContext.manager.beginTransaction()\n      Option(fragmentManagerContext.manager.findFragmentByTag(tagDialog)) foreach ft.remove\n      ft.addToBackStack(javaNull)\n      val dialog = new AlertDialogFragment(\n        message = R.string.removeCollectionMessage,\n        positiveAction = () =>\n          launcherJobs\n            .removeCollection(collection)\n            .resolveAsyncServiceOr(_ => launcherJobs.navigationUiActions.showContactUsError())\n      )\n      dialog.show(ft, tagDialog)\n    }.toService()\n\n  def showDialogForRemoveMoment(momentId: Int) =\n    Ui {\n      val ft = fragmentManagerContext.manager.beginTransaction()\n      Option(fragmentManagerContext.manager.findFragmentByTag(tagDialog)) foreach ft.remove\n      ft.addToBackStack(javaNull)\n      val dialog = new AlertDialogFragment(\n        message = R.string.removeMomentMessage,\n        positiveAction = () =>\n          launcherJobs\n            .removeMoment(momentId)\n            .resolveAsyncServiceOr(_ => launcherJobs.navigationUiActions.showContactUsError())\n      )\n      dialog.show(ft, tagDialog)\n    }.toService()\n\n  def showAddItemMessage(nameCollection: String): TaskService[Unit] =\n    showMessage(R.string.itemAddedToCollectionSuccessful, Seq(nameCollection)).toService()\n\n  def showWidgetCantResizeMessage(): TaskService[Unit] =\n    showMessage(R.string.noResizeForWidget).toService()\n\n  def showWidgetCantMoveMessage(): TaskService[Unit] =\n    showMessage(R.string.noMoveForWidget).toService()\n\n  def showCantRemoveOutAndAboutMessage(): TaskService[Unit] =\n    showMessage(R.string.cantRemoveOutAndAboutMoment).toService()\n\n  def showWidgetNoHaveSpaceMessage(): TaskService[Unit] =\n    showMessage(R.string.noSpaceForWidget).toService()\n\n  def showContactUsError(): TaskService[Unit] =\n    showMessage(R.string.contactUsError).toService()\n\n  def showMinimumOneCollectionMessage(): TaskService[Unit] =\n    showMessage(R.string.minimumOneCollectionMessage).toService()\n\n  def showNoPhoneCallPermissionError(): TaskService[Unit] =\n    showMessage(R.string.noPhoneCallPermissionMessage).toService()\n\n  def showContactPermissionError(action: () => Unit): TaskService[Unit] =\n    showMessageWithAction(R.string.errorContactsPermission, R.string.buttonTryAgain, action)\n      .toService()\n\n  def showCallPermissionError(action: () => Unit): TaskService[Unit] =\n    showMessageWithAction(R.string.errorCallsPermission, R.string.buttonTryAgain, action)\n      .toService()\n\n  def removeActionFragment(): TaskService[Unit] =\n    dom.getFragment match {\n      case Some(fragment) => TaskService.right(removeFragment(fragment))\n      case _              => TaskService.empty\n    }\n\n  def unrevealActionFragment: TaskService[Unit] =\n    dom.getFragment match {\n      case Some(fragment) => fragment.unreveal().toService()\n      case _              => TaskService.empty\n    }\n\n  def goToProfile(): TaskService[Unit] =\n    uiStartIntentForResult(\n      new Intent(activityContextWrapper.bestAvailable, classOf[ProfileActivity]),\n      RequestCodes.goToProfile).toService()\n\n  def goToMomentWorkspace(): TaskService[Unit] = goToWorkspace(pageMoments)\n\n  def goToCollectionWorkspace(): TaskService[Unit] =\n    goToWorkspace(pageCollections)\n\n  def goToWorkspace(page: Int): TaskService[Unit] =\n    ((dom.getData.lift(page) map (data =>\n                                    dom.topBarPanel <~ tblReloadByType(data.workSpaceType)) getOrElse Ui.nop) ~\n      (dom.workspaces <~ lwsSelect(page)) ~\n      (dom.paginationPanel <~ ivReloadPager(page))).toService()\n\n  private[this] def showMessage(res: Int, args: Seq[String] = Seq.empty): Ui[Any] =\n    dom.workspaces <~ vLauncherSnackbar(res, args)\n\n  private[this] def showMessageWithAction(\n      resMessage: Int,\n      resButton: Int,\n      action: () => Unit): Ui[Any] =\n    dom.workspaces <~ vLauncherSnackbarWithAction(\n      resMessage,\n      resButton,\n      action,\n      length = Snackbar.LENGTH_LONG)\n\n  private[this] def showAction[F <: DialogFragment](fragment: F, bundle: Bundle): Ui[Any] = {\n    closeCollectionMenu() ~~\n      Ui {\n        fragment.setArguments(bundle)\n        fragment.show(fragmentManagerContext.manager, tagDialog)\n      }\n  }\n\n  private[this] def closeCollectionMenu(): Ui[Future[Any]] =\n    dom.workspaces <~~ lwsCloseMenu\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/uiactions/TopBarUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs.uiactions\n\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.layouts.tweaks.TopBarLayoutTweaks._\nimport cards.nine.app.ui.components.models.{CollectionsWorkSpace, LauncherData, WorkSpaceType}\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.NineCardsTheme\nimport macroid.{ActivityContextWrapper, FragmentManagerContext, Tweak}\n\nclass TopBarUiActions(val dom: LauncherDOM)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_]) {\n\n  implicit lazy val launcherJobs = createLauncherJobs\n\n  implicit lazy val navigationJobs = createNavigationJobs\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  def initialize(): TaskService[Unit] =\n    (dom.topBarPanel <~ tblInit(CollectionsWorkSpace)).toService()\n\n  def loadBar(data: Seq[LauncherData]): TaskService[Unit] = {\n    val momentType = data.headOption.flatMap(_.moment).flatMap(_.momentType)\n    (dom.topBarPanel <~\n      tblReloadByType(CollectionsWorkSpace) <~\n      (momentType map tblReloadMoment getOrElse Tweak.blank)).toService(Option(\"loadBar\"))\n  }\n\n  def reloadMomentTopBar(): TaskService[Unit] = {\n    val momentType =\n      dom.getData.headOption.flatMap(_.moment).flatMap(_.momentType)\n    (dom.topBarPanel <~ (momentType map tblReloadMoment getOrElse Tweak.blank)).toService()\n  }\n\n  def reloadTopBar(): TaskService[Unit] =\n    (dom.topBarPanel <~ tblReload).toService()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/uiactions/WidgetUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs.uiactions\n\nimport android.appwidget.{AppWidgetHost, AppWidgetManager}\nimport android.content.{ComponentName, Intent}\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport macroid.extras.UIActionsExtras._\nimport macroid.extras.DrawerLayoutTweaks._\nimport cards.nine.app.ui.commons.SafeUi._\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.ops.WidgetsOps.{Cell, _}\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.components.layouts._\nimport cards.nine.app.ui.components.layouts.tweaks.AnimatedWorkSpacesTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.CollectionActionsPanelLayoutTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.LauncherWorkSpacesTweaks._\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.exceptions.SpaceException\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{NineCardsTheme, Widget}\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\nimport macroid.extras.ResourcesExtras.resGetString\n\nclass WidgetUiActions(val dom: LauncherDOM)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_])\n    extends ImplicitsUiExceptions {\n\n  implicit lazy val systemBarsTint = new SystemBarsTint\n\n  implicit lazy val widgetsJobs = createWidgetsJobs\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  val appWidgetMessage = \"Widget Manager not loaded\"\n\n  lazy val actionForWidgets = Seq(\n    CollectionActionItem(\n      resGetString(R.string.remove),\n      R.drawable.icon_launcher_action_remove,\n      WidgetActionRemove))\n\n  def initialize(): TaskService[Unit] = TaskService.right {\n    statuses.appWidgetHost match {\n      case Some(appWidgetHost) => appWidgetHost.startListening()\n      case _                   => AppLog.info(appWidgetMessage)\n    }\n  }\n\n  def destroy(): TaskService[Unit] = TaskService.right {\n    statuses.appWidgetHost match {\n      case Some(appWidgetHost) => appWidgetHost.stopListening()\n      case _                   => AppLog.info(appWidgetMessage)\n    }\n  }\n\n  def addWidgets(widgets: Seq[Widget]): TaskService[Unit] = {\n\n    def add(widget: Widget, appWidgetManager: AppWidgetManager, appWidgetHost: AppWidgetHost) = {\n      val widthContent  = dom.workspaces.getWidth\n      val heightContent = dom.workspaces.getHeight\n\n      val maybeAppWidgetInfo = widget.appWidgetId flatMap (widgetId =>\n                                                             Option(\n                                                               appWidgetManager.getAppWidgetInfo(\n                                                                 widgetId)))\n\n      (maybeAppWidgetInfo, widget.appWidgetId) match {\n        case (Some(appWidgetInfo), Some(appWidgetId)) =>\n          val cell = appWidgetInfo.getCell(widthContent, heightContent)\n\n          Ui {\n            // We must create a wrapper of Ui here because the view must be created in the Ui-Thread\n            val hostView =\n              appWidgetHost\n                .createView(activityContextWrapper.application, appWidgetId, appWidgetInfo)\n            hostView.setAppWidget(appWidgetId, appWidgetInfo)\n            (dom.workspaces <~ lwsAddWidget(hostView, cell, widget)).run\n          }\n        case _ =>\n          val (wCell, hCell) = sizeCell(widthContent, heightContent)\n          dom.workspaces <~ lwsAddNoConfiguredWidget(wCell, hCell, widget)\n      }\n    }\n\n    (statuses.appWidgetManager, statuses.appWidgetHost) match {\n      case (Some(appWidgetManager), Some(appWidgetHost)) =>\n        val uiWidgets = widgets map (widget => add(widget, appWidgetManager, appWidgetHost))\n        Ui.sequence(uiWidgets: _*).toService()\n      case _ => showMessage(R.string.widgetsErrorMessage).toService()\n    }\n  }\n\n  def replaceWidget(widget: Widget): TaskService[Unit] = {\n\n    def replace(appWidgetManager: AppWidgetManager, appWidgetHost: AppWidgetHost) = {\n      val maybeAppWidgetInfo = widget.appWidgetId flatMap (widgetId =>\n                                                             Option(\n                                                               appWidgetManager.getAppWidgetInfo(\n                                                                 widgetId)))\n\n      (maybeAppWidgetInfo, widget.appWidgetId) match {\n        case (Some(appWidgetInfo), Some(appWidgetId)) =>\n          val widthContent  = dom.workspaces.getWidth\n          val heightContent = dom.workspaces.getHeight\n\n          val (wCell, hCell) = sizeCell(widthContent, heightContent)\n\n          Ui {\n            // We must create a wrapper of Ui here because the view must be created in the Ui-Thread\n            val hostView =\n              appWidgetHost\n                .createView(activityContextWrapper.application, appWidgetId, appWidgetInfo)\n            hostView.setAppWidget(appWidgetId, appWidgetInfo)\n            (dom.workspaces <~ lwsReplaceWidget(hostView, wCell, hCell, widget)).run\n          }\n        case _ => Ui.nop\n      }\n    }\n\n    (statuses.appWidgetManager, statuses.appWidgetHost) match {\n      case (Some(appWidgetManager), Some(appWidgetHost)) =>\n        replace(appWidgetManager, appWidgetHost).toService()\n      case _ => showMessage(R.string.widgetsErrorMessage).toService()\n    }\n  }\n\n  def clearWidgets(): TaskService[Unit] =\n    (dom.workspaces <~ lwsClearWidgets()).toService()\n\n  def unhostWidget(id: Int): TaskService[Unit] =\n    (dom.workspaces <~ lwsUnhostWidget(id)).toService()\n\n  def hostWidget(packageName: String, className: String): TaskService[Unit] = {\n    (statuses.appWidgetManager, statuses.appWidgetHost) match {\n      case (Some(appWidgetManager), Some(appWidgetHost)) =>\n        val appWidgetId = appWidgetHost.allocateAppWidgetId()\n        val provider    = new ComponentName(packageName, className)\n        val success =\n          appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, provider)\n        (if (success) {\n           Ui(widgetsJobs.configureOrAddWidget(Some(appWidgetId)).resolveAsync())\n         } else {\n           val intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND)\n           intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)\n           intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, provider)\n           uiStartIntentForResult(intent, RequestCodes.goToWidgets)\n         }).toService()\n      case _ => showMessage(R.string.widgetsErrorMessage).toService()\n    }\n  }\n\n  def configureWidget(appWidgetId: Int): TaskService[Unit] = {\n    statuses.appWidgetManager match {\n      case Some(appWidgetManager) =>\n        val appWidgetInfo = appWidgetManager.getAppWidgetInfo(appWidgetId)\n        (Option(appWidgetInfo.configure) match {\n          case Some(configure) =>\n            val intent =\n              new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE)\n            intent.setComponent(configure)\n            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)\n            uiStartIntentForResult(intent, RequestCodes.goToConfigureWidgets)\n          case _ =>\n            Ui(widgetsJobs.addWidget(Some(appWidgetId)).resolveAsyncServiceOr[Throwable] {\n              case ex: SpaceException =>\n                widgetsJobs.navigationUiActions.showWidgetNoHaveSpaceMessage()\n              case _ =>\n                widgetsJobs.navigationUiActions.showContactUsError()\n            })\n        }).toService()\n      case _ => showMessage(R.string.widgetsErrorMessage).toService()\n    }\n\n  }\n\n  def getWidgetInfoById(appWidgetId: Int): TaskService[Option[(ComponentName, Cell)]] =\n    TaskService {\n      CatchAll[UiException] {\n        statuses.appWidgetManager match {\n          case Some(appWidgetManager) =>\n            Option(appWidgetManager.getAppWidgetInfo(appWidgetId)) map { info =>\n              (info.provider, info.getCell(dom.workspaces.getWidth, dom.workspaces.getHeight))\n            }\n          case _ => None\n        }\n      }\n    }\n\n  def openModeEditWidgets(): TaskService[Unit] =\n    (uiVibrate() ~\n      (dom.dockAppsPanel <~ applyFadeOut()) ~\n      (dom.paginationPanel <~ applyFadeOut()) ~\n      (dom.topBarPanel <~ applyFadeOut()) ~\n      (dom.collectionActionsPanel <~ caplLoad(actionForWidgets) <~ applyFadeIn()) ~\n      (dom.workspaces <~ awsDisabled() <~ lwsStartEditWidgets()) ~\n      (dom.drawerLayout <~ dlLockedClosedStart <~ dlLockedClosedEnd)).toService()\n\n  def reloadViewEditWidgets(): TaskService[Unit] =\n    (dom.workspaces <~ lwsReloadSelectedWidget).toService()\n\n  def closeModeEditWidgets(): TaskService[Unit] =\n    ((dom.dockAppsPanel <~ applyFadeIn()) ~\n      (dom.paginationPanel <~ applyFadeIn()) ~\n      (dom.topBarPanel <~ applyFadeIn()) ~\n      (dom.collectionActionsPanel <~ applyFadeOut()) ~\n      (dom.workspaces <~ awsEnabled() <~ lwsCloseEditWidgets()) ~\n      (dom.drawerLayout <~\n        dlUnlockedStart <~\n        (if (dom.hasCurrentMomentAssociatedCollection) dlUnlockedEnd\n         else Tweak.blank))).toService()\n\n  def cancelWidget(appWidgetId: Int): TaskService[Unit] = TaskService.right {\n    statuses.appWidgetHost match {\n      case Some(appWidgetHost) => appWidgetHost.deleteAppWidgetId(appWidgetId)\n      case _                   => TaskService.right(AppLog.info(appWidgetMessage))\n    }\n  }\n\n  def editWidgetsShowActions(): TaskService[Unit] =\n    (dom.workspaces <~ lwsReloadSelectedWidget).toService()\n\n  private[this] def showMessage(res: Int, args: Seq[String] = Seq.empty): Ui[Any] =\n    dom.workspaces <~ vLauncherSnackbar(res, args)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/jobs/uiactions/WorkspaceUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.jobs.uiactions\n\nimport android.graphics.Color\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.widget.ImageView\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.dialogs.wizard.{LauncherWizardInline, WizardInlinePreferences}\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.commons.{SystemBarsTint, UiContext}\nimport cards.nine.app.ui.components.drawables.EdgeWorkspaceDrawable\nimport cards.nine.app.ui.components.layouts.tweaks.AnimatedWorkSpacesTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.AppsMomentLayoutTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.LauncherWorkSpacesTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.TopBarLayoutTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.WorkSpaceItemMenuTweaks._\nimport cards.nine.app.ui.components.layouts.{\n  AnimatedWorkSpacesListener,\n  LauncherWorkSpacesListener,\n  WorkspaceItemMenu\n}\nimport cards.nine.app.ui.components.models.{\n  CollectionsWorkSpace,\n  LauncherData,\n  MomentWorkSpace,\n  WorkSpaceType\n}\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.{EditWidgetsMode, NormalMode}\nimport cards.nine.app.ui.launcher.jobs.{LauncherJobs, NavigationJobs, WidgetsJobs}\nimport cards.nine.app.ui.launcher.snails.LauncherSnails._\nimport cards.nine.app.ui.preferences.commons.IsDeveloper\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.ConditionWeather\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\nimport macroid.extras.DrawerLayoutTweaks._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.LinearLayoutTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.UIActionsExtras._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\n\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.Future\n\nclass WorkspaceUiActions(val dom: LauncherDOM)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_]) {\n\n  // TODO We select the page in ViewPager with collections. In the future this will be a user preference\n  val selectedPageDefault = 1\n\n  implicit lazy val systemBarsTint = new SystemBarsTint\n\n  lazy val wizardInlinePreferences = new WizardInlinePreferences()\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  implicit lazy val launcherJobs: LauncherJobs = createLauncherJobs\n\n  implicit lazy val navigationJobs: NavigationJobs = createNavigationJobs\n\n  implicit lazy val widgetsJobs: WidgetsJobs = createWidgetsJobs\n\n  val maxBackgroundPercent: Float = 0.7f\n\n  val typeWorkspaceButtonKey = \"type-workspace-button-key\"\n\n  val collectionId = \"collectionId\"\n\n  def initialize(): TaskService[Unit] = {\n\n    def goToSettings(): Ui[Any] =\n      closeWorkspaceMenu() ~~ Ui(\n        navigationJobs.navigationUiActions.launchSettings().resolveAsync())\n\n    ((dom.paginationPanel <~ On.longClick(openBackgroundMenu() ~ Ui(true))) ~\n      (dom.workspacesEdgeLeft <~ vBackground(new EdgeWorkspaceDrawable(left = true))) ~\n      (dom.workspacesEdgeRight <~ vBackground(new EdgeWorkspaceDrawable(left = false))) ~\n      (dom.workspaces <~\n        lwsListener(\n          LauncherWorkSpacesListener(\n            onStartOpenMenu = startBackgroundMenu,\n            onUpdateOpenMenu = updateBackgroundMenu,\n            onEndOpenMenu = endBackgroundMenu)\n        ) <~\n        awsListener(AnimatedWorkSpacesListener(onClick = () => {\n          ((statuses.mode, statuses.transformation) match {\n            case (NormalMode, _) => navigationJobs.menuDrawersUiActions.openAppsMoment()\n            case (EditWidgetsMode, Some(_)) =>\n              statuses = statuses.copy(transformation = None)\n              navigationJobs.widgetUiActions.reloadViewEditWidgets()\n            case (EditWidgetsMode, None) =>\n              widgetsJobs.closeModeEditWidgets()\n            case _ => TaskService.empty\n          }).resolveAsync()\n        }, onLongClick = () => openBackgroundMenu().run))) ~\n      (dom.menuWorkspaceContent <~ vgAddViews(getItemsForFabMenu)) ~\n      (dom.menuLauncherWallpaper <~ On.click {\n        closeWorkspaceMenu() ~~ Ui(\n          navigationJobs.navigationUiActions.launchWallpaper().resolveAsync())\n      }) ~\n      (dom.menuLauncherWidgets <~ On.click {\n        closeWorkspaceMenu() ~~ Ui(navigationJobs.launchWidgets().resolveAsync())\n      }) ~\n      (dom.menuLauncherSettings <~ On.click {\n        goToSettings()\n      } <~ On.longClick {\n        Ui(IsDeveloper.convertToDeveloper) ~\n          uiShortToast(R.string.developerOptionsActivated) ~\n          goToSettings() ~\n          Ui(true)\n      })).toService()\n  }\n\n  def reloadMoment(data: LauncherData): TaskService[Unit] = {\n    val momentType     = data.moment.flatMap(_.momentType)\n    val launcherMoment = data.moment\n    ((dom.workspaces <~ lwsDataMoment(data)) ~\n      (dom.appsMoment <~ (launcherMoment map amlPopulate getOrElse Tweak.blank)) ~\n      (dom.topBarPanel <~ (momentType map tblReloadMoment getOrElse Tweak.blank))).toService()\n  }\n\n  def showWeather(condition: ConditionWeather): TaskService[Unit] =\n    (dom.topBarPanel <~ tblWeather(condition)).toService()\n\n  def loadLauncherInfo(data: Seq[LauncherData]): TaskService[Unit] = {\n    ((dom.loading <~ vGone) ~\n      (dom.workspaces <~\n        vGlobalLayoutListener(\n          _ =>\n            (dom.workspaces <~\n              lwsData(data, selectedPageDefault) <~\n              lwsAddMovementObserver(dom.topBarPanel.movement) <~\n              awsAddPageChangedObserver(currentPage => {\n                (dom.paginationPanel <~ ivReloadPager(currentPage)).run\n              })) ~\n              createPager(selectedPageDefault)))).toService(Option(\"loadLauncherInfo\"))\n  }\n\n  def closeBackgroundMenu(): TaskService[Unit] = closeWorkspaceMenu().toService()\n\n  def reloadWorkspaces(data: Seq[LauncherData], page: Option[Int] = None): TaskService[Unit] =\n    ((dom.workspaces <~ lwsDataCollections(data, page)) ~ reloadWorkspacePager).toService()\n\n  def cleanWorkspaces(): TaskService[Unit] = (dom.workspaces <~ lwsClean).toService()\n\n  def openLauncherWizardInline(): TaskService[Unit] =\n    (if (wizardInlinePreferences.shouldBeShowed(LauncherWizardInline)) {\n       dom.workspaces <~ vLauncherWizardSnackbar(LauncherWizardInline)\n     } else {\n       Ui.nop\n     }).toService()\n\n  private[this] def openBackgroundMenu(): Ui[Any] = dom.workspaces <~ lwsOpenMenu\n\n  private[this] def closeWorkspaceMenu(): Ui[Future[Any]] =\n    (dom.drawerLayout <~\n      dlUnlockedStart <~\n      (if (dom.hasCurrentMomentAssociatedCollection) dlUnlockedEnd else Tweak.blank)) ~\n      (dom.workspaces <~~ lwsCloseMenu)\n\n  private[this] def reloadWorkspacePager: Ui[Any] =\n    createPager((dom.workspaces ~> lwsCurrentPage()).get)\n\n  private[this] def createPager(activePosition: Int): Ui[Any] = {\n    def pagination(position: Int) = {\n      val margin = resGetDimensionPixelSize(R.dimen.margin_pager_collection)\n      (w[ImageView] <~\n        vWrapContent <~\n        llLayoutMargin(marginLeft = margin, marginTop = 0, marginRight = margin, marginBottom = 0) <~\n        ivSrc(R.drawable.workspaces_pager) <~ vSetPosition(position)).get\n    }\n\n    val pagerViews = 0 until dom.getWorksSpacesCount map { position =>\n      val view = pagination(position)\n      view.setActivated(activePosition == position)\n      view\n    }\n    dom.paginationPanel <~ vgRemoveAllViews <~ vgAddViews(pagerViews)\n  }\n\n  private[this] def startBackgroundMenu(): Ui[Any] = {\n\n    def showItemsWorkspace(workspaceType: WorkSpaceType) = Transformer {\n      case item: WorkspaceItemMenu\n          if item.getField[WorkSpaceType](typeWorkspaceButtonKey).contains(workspaceType) =>\n        item <~ vVisible\n      case item: WorkspaceItemMenu => item <~ vGone\n    }\n\n    val height                = dom.menuLauncherContent.getHeight + systemBarsTint.getNavigationBarHeight\n    val isCollectionWorkspace = (dom.workspaces ~> lwsIsCollectionWorkspace).get\n    val workspaceType         = if (isCollectionWorkspace) CollectionsWorkSpace else MomentWorkSpace\n    (dom.menuCollectionRoot <~ vVisible <~ vClearClick) ~\n      (dom.menuWorkspaceContent <~ showItemsWorkspace(workspaceType) <~ vAlpha(0) <~ vTranslationY(\n        height)) ~\n      (dom.menuLauncherContent <~ vTranslationY(height)) ~\n      (dom.dockAppsPanel <~ fade(out = true)) ~\n      (dom.paginationPanel <~ fade(out = true)) ~\n      (dom.topBarPanel <~ fade(out = true))\n  }\n\n  private[this] def updateBackgroundMenu(percent: Float): Ui[Any] = {\n    val backgroundPercent = maxBackgroundPercent * percent\n    val colorBackground   = Color.BLACK.alpha(backgroundPercent)\n    val height            = dom.menuLauncherContent.getHeight + systemBarsTint.getNavigationBarHeight\n    val translate         = height - (height * percent)\n    (dom.menuCollectionRoot <~ vBackgroundColor(colorBackground)) ~\n      (dom.menuLauncherContent <~ vTranslationY(translate)) ~\n      (dom.menuWorkspaceContent <~ vAlpha(percent) <~ vTranslationY(translate))\n  }\n\n  private[this] def endBackgroundMenu(opened: Boolean): Ui[Any] =\n    if (opened) {\n      (dom.menuCollectionRoot <~ On.click(closeWorkspaceMenu())) ~\n        (dom.drawerLayout <~ dlLockedClosedStart <~ dlLockedClosedEnd)\n    } else {\n      (dom.dockAppsPanel <~ fade()) ~\n        (dom.paginationPanel <~ fade()) ~\n        (dom.topBarPanel <~ fade()) ~\n        (dom.menuCollectionRoot <~ vGone) ~\n        (dom.drawerLayout <~ dlUnlockedStart <~ dlUnlockedEnd)\n    }\n\n  private[this] def getItemsForFabMenu = Seq(\n    (w[WorkspaceItemMenu] <~\n      workspaceButtonCreateCollectionStyle <~\n      vAddField(typeWorkspaceButtonKey, CollectionsWorkSpace) <~\n      On.click {\n        Ui {\n          navigationJobs.launchCreateOrCollection().resolveAsync()\n        }\n      }).get,\n    (w[WorkspaceItemMenu] <~\n      workspaceButtonMyCollectionsStyle <~\n      vAddField(typeWorkspaceButtonKey, CollectionsWorkSpace) <~\n      On.click {\n        Ui {\n          navigationJobs.launchPrivateCollection().resolveAsync()\n        }\n      }).get,\n    (w[WorkspaceItemMenu] <~\n      workspaceButtonPublicCollectionStyle <~\n      vAddField(typeWorkspaceButtonKey, CollectionsWorkSpace) <~\n      On.click {\n        Ui {\n          navigationJobs.launchPublicCollection().resolveAsync()\n        }\n      }).get,\n    (w[WorkspaceItemMenu] <~\n      workspaceButtonEditMomentStyle <~\n      vAddField(typeWorkspaceButtonKey, MomentWorkSpace) <~\n      On.click {\n        val momentType = dom.getCurrentMomentTypeName\n        momentType match {\n          case Some(moment) =>\n            Ui {\n              navigationJobs.launchEditMoment(moment).resolveAsync()\n            }\n          case _ => Ui.nop\n        }\n      }).get,\n    (w[WorkspaceItemMenu] <~\n      workspaceButtonChangeMomentStyle <~\n      vAddField(typeWorkspaceButtonKey, MomentWorkSpace) <~\n      On.click {\n        closeWorkspaceMenu() ~~ Ui(navigationJobs.goToChangeMoment().resolveAsync())\n      }).get,\n    (w[WorkspaceItemMenu] <~\n      workspaceButtonAddMomentStyle <~\n      vAddField(typeWorkspaceButtonKey, MomentWorkSpace) <~\n      On.click {\n        val momentType = dom.getCurrentMomentTypeName\n        momentType match {\n          case Some(moment) =>\n            Ui {\n              navigationJobs.launchAddMoment().resolveAsync()\n            }\n          case _ => Ui.nop\n        }\n      }).get\n  )\n\n  // Styles\n\n  private[this] def workspaceButtonCreateCollectionStyle(\n      implicit context: ContextWrapper): Tweak[WorkspaceItemMenu] =\n    wimPopulate(\n      resGetColor(R.color.collection_fab_button_item_1),\n      R.drawable.fab_menu_icon_create_new_collection,\n      R.string.createNewCollection)\n\n  private[this] def workspaceButtonMyCollectionsStyle(\n      implicit context: ContextWrapper): Tweak[WorkspaceItemMenu] =\n    wimPopulate(\n      resGetColor(R.color.collection_fab_button_item_2),\n      R.drawable.fab_menu_icon_my_collections,\n      R.string.myCollections)\n\n  private[this] def workspaceButtonPublicCollectionStyle(\n      implicit context: ContextWrapper): Tweak[WorkspaceItemMenu] =\n    wimPopulate(\n      resGetColor(R.color.collection_fab_button_item_3),\n      R.drawable.fab_menu_icon_public_collections,\n      R.string.publicCollections)\n\n  private[this] def workspaceButtonEditMomentStyle(\n      implicit context: ContextWrapper): Tweak[WorkspaceItemMenu] =\n    wimPopulate(\n      resGetColor(R.color.collection_fab_button_item_1),\n      R.drawable.fab_menu_icon_edit_moment,\n      R.string.editMoment)\n\n  private[this] def workspaceButtonChangeMomentStyle(\n      implicit context: ContextWrapper): Tweak[WorkspaceItemMenu] =\n    wimPopulate(\n      resGetColor(R.color.collection_fab_button_item_2),\n      R.drawable.fab_menu_icon_change_moment,\n      R.string.changeMoment)\n\n  private[this] def workspaceButtonAddMomentStyle(\n      implicit context: ContextWrapper): Tweak[WorkspaceItemMenu] =\n    wimPopulate(\n      resGetColor(R.color.collection_fab_button_item_3),\n      R.drawable.fab_menu_icon_add_moment,\n      R.string.addMoment)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/snails/DrawerSnails.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.snails\n\nimport android.animation.{Animator, AnimatorListenerAdapter}\nimport android.annotation.TargetApi\nimport android.os.Build\nimport android.view.animation.DecelerateInterpolator\nimport android.view.{View, ViewAnimationUtils}\nimport macroid.extras.SnailsUtils._\nimport cards.nine.app.ui.commons.ops.ViewOps._\nimport cards.nine.app.ui.preferences.commons.{\n  AppDrawerAnimationCircle,\n  AppDrawerAnimationValue,\n  SpeedAnimations\n}\nimport cards.nine.commons._\nimport macroid.extras.SnailsUtils\nimport macroid.{ContextWrapper, Snail}\n\nimport scala.concurrent.Promise\n\nobject DrawerSnails {\n\n  def openAppDrawer(animation: AppDrawerAnimationValue, source: View)(\n      implicit context: ContextWrapper): Snail[View] = Snail[View] { view =>\n    view.clearAnimation()\n    view.setLayerType(View.LAYER_TYPE_HARDWARE, javaNull)\n    val animPromise = Promise[Unit]()\n\n    animation match {\n      case anim @ AppDrawerAnimationCircle if anim.isSupported =>\n        reveal(source, view)(animPromise.trySuccess(()))\n      case _ => fadeIn(view)(animPromise.trySuccess(()))\n    }\n\n    animPromise.future\n  }\n\n  def closeAppDrawer(animation: AppDrawerAnimationValue, source: View)(\n      implicit context: ContextWrapper): Snail[View] = Snail[View] { view =>\n    view.clearAnimation()\n    view.setLayerType(View.LAYER_TYPE_HARDWARE, javaNull)\n    val animPromise = Promise[Unit]()\n\n    animation match {\n      case anim @ AppDrawerAnimationCircle if anim.isSupported =>\n        reveal(source, view, in = false)(animPromise.trySuccess(()))\n      case _ => fadeOut(view)(animPromise.trySuccess(()))\n    }\n\n    animPromise.future\n  }\n\n  @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n  private[this] def reveal(source: View, view: View, in: Boolean = true)(\n      animationEnd: => Unit = ())(implicit context: ContextWrapper): Unit = {\n    val (cx, cy)   = source.calculateAnchorViewPosition\n    val fromRadius = source.getWidth / 2\n    val toRadius   = SnailsUtils.calculateRadius(width = cx + fromRadius, height = cy + fromRadius)\n\n    val (startRadius, endRadius): (Float, Float) =\n      if (in) (fromRadius, toRadius) else (toRadius, fromRadius)\n\n    val reveal: Animator = ViewAnimationUtils\n      .createCircularReveal(view, cx + fromRadius, cy + fromRadius, startRadius, endRadius)\n    reveal.setDuration(SpeedAnimations.getDuration)\n    reveal.addListener(new AnimatorListenerAdapter {\n      override def onAnimationStart(animation: Animator): Unit = {\n        super.onAnimationStart(animation)\n        if (in) {\n          view.setAlpha(1)\n          view.setVisibility(View.VISIBLE)\n        }\n      }\n\n      override def onAnimationEnd(animation: Animator) = {\n        super.onAnimationEnd(animation)\n        if (!in) view.setVisibility(View.GONE)\n        view.setLayerType(View.LAYER_TYPE_NONE, javaNull)\n        animationEnd\n      }\n    })\n    reveal.setInterpolator(new DecelerateInterpolator)\n    reveal.start()\n  }\n\n  private[this] def fadeIn(view: View)(animationEnd: => Unit = ())(\n      implicit context: ContextWrapper): Unit = {\n    view.setAlpha(0f)\n    view\n      .animate()\n      .setDuration(SpeedAnimations.getDuration)\n      .setInterpolator(new DecelerateInterpolator)\n      .alpha(1f)\n      .setListener(new AnimatorListenerAdapter {\n        override def onAnimationStart(animation: Animator): Unit = {\n          super.onAnimationStart(animation)\n          view.setVisibility(View.VISIBLE)\n        }\n\n        override def onAnimationEnd(animation: Animator) = {\n          super.onAnimationEnd(animation)\n          view.setLayerType(View.LAYER_TYPE_NONE, javaNull)\n          animationEnd\n        }\n      })\n      .start()\n  }\n\n  private[this] def fadeOut(view: View)(animationEnd: => Unit = ())(\n      implicit context: ContextWrapper): Unit = {\n    view.setAlpha(1f)\n    view\n      .animate()\n      .setDuration(SpeedAnimations.getDuration)\n      .setInterpolator(new DecelerateInterpolator)\n      .alpha(0f)\n      .setListener(new AnimatorListenerAdapter {\n        override def onAnimationEnd(animation: Animator) {\n          super.onAnimationEnd(animation)\n          view.setVisibility(View.GONE)\n          view.setLayerType(View.LAYER_TYPE_NONE, javaNull)\n          animationEnd\n        }\n      })\n      .start()\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/snails/LauncherSnails.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.snails\n\nimport android.animation.{Animator, AnimatorListenerAdapter}\nimport android.view.View\nimport macroid.extras.ResourcesExtras._\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.{ContextWrapper, Snail}\n\nimport scala.concurrent.Promise\n\nobject LauncherSnails {\n\n  def pagerAppear(implicit context: ContextWrapper): Snail[View] =\n    Snail[View] { view =>\n      val duration = resGetInteger(R.integer.anim_duration_pager_appear)\n      view.clearAnimation()\n      view.setLayerType(View.LAYER_TYPE_HARDWARE, javaNull)\n      val animPromise = Promise[Unit]()\n      view.setScaleX(.7f)\n      view.setScaleY(.7f)\n      view.setAlpha(.7f)\n      view.animate\n        .alpha(1)\n        .scaleX(1)\n        .scaleY(1)\n        .setDuration(duration)\n        .setListener(new AnimatorListenerAdapter {\n          override def onAnimationEnd(animation: Animator) = {\n            super.onAnimationEnd(animation)\n            view.setLayerType(View.LAYER_TYPE_NONE, javaNull)\n            animPromise.trySuccess(())\n          }\n        })\n        .start()\n      animPromise.future\n    }\n\n  def fade(out: Boolean = false)(implicit context: ContextWrapper): Snail[View] = {\n    val duration = resGetInteger(R.integer.anim_duration_pager_appear)\n    if (out) {\n      applyFadeOut(Some(duration))\n    } else {\n      applyFadeIn(Some(duration))\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/types/AppDrawerIconShadowBuilder.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.types\n\nimport android.graphics.{Canvas, Point}\nimport android.view.View\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nclass AppDrawerIconShadowBuilder(view: View)(implicit contextWrapper: ContextWrapper)\n    extends View.DragShadowBuilder(view) {\n\n  val size: Int =\n    (resGetDimensionPixelSize(R.dimen.size_icon_app_medium) * 1.2f).toInt\n\n  val scale: Float = size.toFloat / view.getWidth.toFloat\n\n  override def onProvideShadowMetrics(shadowSize: Point, shadowTouchPoint: Point): Unit = {\n    shadowSize.set(size, size)\n    shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2)\n  }\n\n  override def onDrawShadow(canvas: Canvas): Unit = {\n    Option(getView) foreach { view =>\n      canvas.save()\n      canvas.scale(scale, scale)\n      view.draw(canvas)\n      canvas.restore()\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/types/CollectionShadowBuilder.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.types\n\nimport android.graphics.Canvas\nimport android.view.View\n\nclass CollectionShadowBuilder(view: View) extends View.DragShadowBuilder(view) {\n\n  override def onDrawShadow(canvas: Canvas): Unit = {\n    Option(getView) foreach { view =>\n      view.draw(canvas)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/types/DragLauncherType.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.types\n\nsealed trait DragLauncherType {\n  val name: String\n}\n\ncase object ReorderCollection extends DragLauncherType {\n  override val name: String = \"reorder\"\n}\n\ncase object ReorderWidget extends DragLauncherType {\n  override val name: String = \"reorder-widget\"\n}\n\ncase object AddItemToCollection extends DragLauncherType {\n  override val name: String = \"add-item\"\n}\n\ncase object UnknownLauncherType extends DragLauncherType {\n  override val name: String = \"unknown\"\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/types/DragObject.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.types\n\nimport android.view.View.DragShadowBuilder\n\ncase class DragObject(shadow: DragShadowBuilder, action: DragLauncherType)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/types/MenuOptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.types\n\nsealed trait AppsMenuOption {\n  val name: String\n}\n\ncase object AppsAlphabetical extends AppsMenuOption {\n  override val name: String = \"AppsAlphabetical\"\n}\n\ncase object AppsByCategories extends AppsMenuOption {\n  override val name: String = \"AppsByCategories\"\n}\n\ncase object AppsByLastInstall extends AppsMenuOption {\n  override val name: String = \"AppsByLastInstall\"\n}\n\nobject AppsMenuOption {\n  val list = Seq(AppsAlphabetical, AppsByCategories, AppsByLastInstall)\n\n  def apply(o: AppsMenuOption): Int = list.indexOf(o)\n\n  def apply(status: String): Option[AppsMenuOption] =\n    list find (_.name == status)\n}\n\nsealed trait ContactsMenuOption {\n  val name: String\n}\n\ncase object ContactsAlphabetical extends ContactsMenuOption {\n  override val name: String = \"ContactsAlphabetical\"\n}\n\ncase object ContactsFavorites extends ContactsMenuOption {\n  override val name: String = \"ContactsFavorites\"\n}\n\ncase object ContactsByLastCall extends ContactsMenuOption {\n  override val name: String = \"ContactsByLastCall\"\n}\n\nobject ContactsMenuOption {\n  val list = Seq(ContactsAlphabetical, ContactsFavorites, ContactsByLastCall)\n\n  def apply(o: ContactsMenuOption): Int = list.indexOf(o)\n\n  def apply(status: String): Option[ContactsMenuOption] =\n    list find (_.name == status)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/launcher/types/WidgetShadowBuilder.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.launcher.types\n\nimport android.graphics.Canvas\nimport android.view.View\n\nclass WidgetShadowBuilder(view: View) extends View.DragShadowBuilder(view) {\n\n  override def onDrawShadow(canvas: Canvas): Unit = {\n    Option(getView) foreach { view =>\n      view.draw(canvas)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/NineCardsPreferencesActivity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences\n\nimport android.app.{ActionBar, Activity}\nimport android.os.Bundle\nimport android.preference.Preference.OnPreferenceClickListener\nimport android.preference.{Preference, PreferenceActivity, PreferenceFragment}\nimport android.view.MenuItem\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.preferences.about.AboutFragment\nimport cards.nine.app.ui.preferences.analytics.AnalyticsFragment\nimport cards.nine.app.ui.preferences.animations.AnimationsFragment\nimport cards.nine.app.ui.preferences.appdrawer.AppDrawerFragment\nimport cards.nine.app.ui.preferences.commons._\nimport cards.nine.app.ui.preferences.developers.DeveloperFragment\nimport cards.nine.app.ui.preferences.lookandfeel.LookFeelFragment\nimport cards.nine.app.ui.preferences.moments.MomentsFragment\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.Contexts\n\nclass NineCardsPreferencesActivity\n    extends PreferenceActivity\n    with PreferencesDOM\n    with Contexts[Activity] {\n\n  override lazy val actionBar: Option[ActionBar] = Option(getActionBar)\n\n  lazy val ui = new PreferencesUiActions(this)\n\n  lazy val jobs = new PreferencesJobs(ui)\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n    jobs.initialize().resolveAsync()\n    getFragmentManager\n      .beginTransaction()\n      .replace(android.R.id.content, new NineCardsPreferenceFragment())\n      .commit()\n  }\n\n  override def onOptionsItemSelected(item: MenuItem): Boolean =\n    item.getItemId match {\n      case android.R.id.home =>\n        super.onBackPressed()\n        jobs.initializeActionBarTitle().resolveAsync()\n        true\n      case _ =>\n        super.onOptionsItemSelected(item)\n    }\n\n  override def onBackPressed() = {\n    super.onBackPressed()\n    jobs.initializeActionBarTitle().resolveAsync()\n  }\n\n  def preferenceChanged(prefName: String): Unit =\n    jobs.preferenceChanged(prefName).resolveAsync()\n\n  class NineCardsPreferenceFragment extends PreferenceFragment {\n\n    override def onCreate(savedInstanceState: Bundle) = {\n      super.onCreate(savedInstanceState)\n\n      if (IsDeveloper.readValue) {\n        addPreferencesFromResource(R.xml.preferences_devs_headers)\n        findPreference(DeveloperPreferences.name).setOnPreferenceClickListener(\n          preferenceClick(DeveloperPreferences.name, new DeveloperFragment()))\n      } else {\n        addPreferencesFromResource(R.xml.preferences_headers)\n      }\n\n      findPreference(LookFeelPreferences.name).setOnPreferenceClickListener(\n        preferenceClick(LookFeelPreferences.name, new LookFeelFragment()))\n\n      findPreference(MomentsPreferences.name).setOnPreferenceClickListener(\n        preferenceClick(MomentsPreferences.name, new MomentsFragment()))\n\n      findPreference(AppDrawerPreferences.name).setOnPreferenceClickListener(\n        preferenceClick(AppDrawerPreferences.name, new AppDrawerFragment()))\n\n      findPreference(AnimationsPreferences.name).setOnPreferenceClickListener(\n        preferenceClick(AnimationsPreferences.name, new AnimationsFragment()))\n\n      findPreference(AnalyticsPreferences.name).setOnPreferenceClickListener(\n        preferenceClick(AnalyticsPreferences.name, new AnalyticsFragment()))\n\n      findPreference(WizardInlinePreferences.name).setOnPreferenceClickListener(\n        preferenceActionClick(() => jobs.cleanWizardInlinePreferences().resolveAsync()))\n\n      findPreference(AboutPreferences.name)\n        .setOnPreferenceClickListener(preferenceClick(AboutPreferences.name, new AboutFragment()))\n\n      findPreference(FeedbackPreferences.name).setOnPreferenceClickListener(\n        preferenceActionClick(() => ui.goToFeedback().resolveAsync()))\n\n      findPreference(HelpPreferences.name).setOnPreferenceClickListener(preferenceActionClick(() =>\n        ui.goToHelp().resolveAsync()))\n    }\n\n    private[this] def preferenceClick(key: String, fragment: PreferenceFragment) =\n      new OnPreferenceClickListener {\n        override def onPreferenceClick(preference: Preference): Boolean = {\n          getFragmentManager\n            .beginTransaction()\n            .addToBackStack(key)\n            .replace(android.R.id.content, fragment)\n            .commit()\n          true\n        }\n      }\n\n    private[this] def preferenceActionClick(action: () => Unit) =\n      new OnPreferenceClickListener {\n        override def onPreferenceClick(preference: Preference): Boolean = {\n          action()\n          true\n        }\n      }\n\n  }\n\n}\n\ntrait PreferencesDOM {\n\n  def actionBar: Option[ActionBar]\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/PreferencesJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences\n\nimport android.content.Intent\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.commons.dialogs.wizard.WizardInlinePreferences\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport macroid.ActivityContextWrapper\n\nclass PreferencesJobs(ui: PreferencesUiActions)(implicit contextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with ImplicitsUiExceptions {\n\n  var statuses = PreferencesJobsStatuses()\n\n  lazy val wizardInlinePreferences = new WizardInlinePreferences()\n\n  def initialize(): TaskService[Unit] = ui.initialize()\n\n  def initializeActionBarTitle(): TaskService[Unit] = ui.setActionBarTitle()\n\n  def preferenceChanged(preferenceName: String): TaskService[Unit] = {\n    statuses = statuses.copy(changedPreferences = statuses.changedPreferences + preferenceName)\n    val data = new Intent()\n    data.putExtra(ResultData.preferencesResultData, statuses.changedPreferences.toArray)\n    ui.setActivityResult(ResultCodes.preferencesChanged, data)\n  }\n\n  def cleanWizardInlinePreferences(): TaskService[Unit] =\n    for {\n      _ <- TaskService.right(wizardInlinePreferences.clean())\n      _ <- ui.showWizardInlineCleaned()\n    } yield ()\n\n}\n\ncase class PreferencesJobsStatuses(changedPreferences: Set[String] = Set.empty)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/PreferencesUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences\n\nimport android.content.Intent\nimport macroid.extras.UIActionsExtras._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.commons.services.TaskService._\nimport macroid.extras.ResourcesExtras._\nimport cards.nine.app.ui.commons.SafeUi._\nimport com.apptentive.android.sdk.Apptentive\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.{ActivityContextWrapper, Ui}\n\nclass PreferencesUiActions(dom: PreferencesDOM)(implicit contextWrapper: ActivityContextWrapper) {\n\n  def initialize(): TaskService[Unit] =\n    Ui {\n      dom.actionBar foreach { ab =>\n        ab.setDisplayHomeAsUpEnabled(true)\n        ab.setDisplayShowHomeEnabled(false)\n        ab.setDisplayShowTitleEnabled(true)\n        ab.setDisplayUseLogoEnabled(false)\n      }\n    }.toService()\n\n  def setActionBarTitle(): TaskService[Unit] =\n    Ui(dom.actionBar foreach (_.setTitle(R.string.nineCardsSettingsTitle))).toService()\n\n  def setActivityResult(resultCode: Int, data: Intent): TaskService[Unit] =\n    Ui(contextWrapper.original.get foreach (_.setResult(resultCode, data))).toService()\n\n  def showContactUsError(): TaskService[Unit] =\n    uiShortToast(R.string.contactUsError).toService()\n\n  def showWizardInlineCleaned(): TaskService[Unit] =\n    uiShortToast(R.string.wizardInlineCleaned).toService()\n\n  def goToHelp(): TaskService[Unit] =\n    uiOpenUrlIntent(resGetString(R.string.ninecards_help)).toService()\n\n  def goToFeedback(): TaskService[Unit] =\n    Ui(Apptentive.showMessageCenter(contextWrapper.bestAvailable)).toService()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/about/AboutFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.about\n\nimport android.app.Fragment\nimport android.os.Bundle\nimport android.preference.Preference.OnPreferenceClickListener\nimport android.preference.{Preference, PreferenceCategory, PreferenceFragment}\nimport cards.nine.app.ui.preferences.commons.FindPreferences\nimport cards.nine.app.ui.commons.SafeUi._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.Contexts\n\nclass AboutFragment extends PreferenceFragment with Contexts[Fragment] with FindPreferences {\n\n  val dom = AboutDOM(this)\n\n  val technologies = Seq(\n    Library(\n      \"Scala\",\n      R.drawable.tech_scala,\n      \"https://www.scala-lang.org/\",\n      R.string.server_and_client),\n    Library(\n      \"Cats\",\n      R.drawable.tech_cats,\n      \"http://typelevel.org/cats/\",\n      R.string.server_and_client),\n    Library(\"Monix\", R.drawable.tech_monix, \"https://monix.io/\", R.string.server_and_client),\n    Library(\n      \"Macroid\",\n      R.drawable.tech_macroid,\n      \"http://47deg.github.io/macroid/\",\n      R.string.only_client),\n    Library(\"Spray\", R.drawable.tech_spray, \"http://spray.io/\", R.string.only_server),\n    Library(\"Akka\", R.drawable.tech_akka, \"http://akka.io/\", R.string.only_server),\n    Library(\n      \"Circe\",\n      R.drawable.tech_circe,\n      \"https://circe.github.io/circe/\",\n      R.string.only_server),\n    Library(\n      \"Doobie\",\n      R.drawable.tech_doobie,\n      \"https://github.com/tpolecat/doobie\",\n      R.string.only_server),\n    Library(\n      \"Shapeless\",\n      R.drawable.tech_shapeless,\n      \"https://github.com/milessabin/shapeless\",\n      R.string.only_server))\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n    Option(getActivity.getActionBar) foreach (_.setTitle(getString(R.string.aboutTitle)))\n    addPreferencesFromResource(R.xml.preferences_about)\n\n    technologies foreach { tech =>\n      val preference = new Preference(fragmentContextWrapper.bestAvailable)\n      preference.setTitle(tech.name)\n      preference.setSummary(tech.summary)\n      preference.setIcon(tech.icon)\n      preference.setOnPreferenceClickListener(new OnPreferenceClickListener {\n        override def onPreferenceClick(preference: Preference): Boolean = {\n          uiOpenUrlIntent(tech.url).run\n          true\n        }\n      })\n      dom.aboutPreferenceCategory.addPreference(preference)\n    }\n  }\n\n}\n\ncase class AboutDOM(dom: FindPreferences) {\n\n  def aboutPreferenceCategory = dom.findByName[PreferenceCategory](\"libraries\")\n}\n\ncase class Library(name: String, icon: Int, url: String, summary: Int)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/analytics/AnalyticsFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.analytics\n\nimport android.os.Bundle\nimport cards.nine.app.ui.preferences.commons.PreferenceChangeListenerFragment\nimport com.fortysevendeg.ninecardslauncher.R\n\nclass AnalyticsFragment extends PreferenceChangeListenerFragment {\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n    withActivity { activity =>\n      Option(activity.getActionBar) foreach (_.setTitle(getString(R.string.analyticsPrefTitle)))\n    }\n    addPreferencesFromResource(R.xml.preferences_analytics)\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/animations/AnimationsFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.animations\n\nimport android.os.Bundle\nimport cards.nine.app.ui.preferences.commons.PreferenceChangeListenerFragment\nimport com.fortysevendeg.ninecardslauncher.R\n\nclass AnimationsFragment extends PreferenceChangeListenerFragment {\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n    Option(getActivity.getActionBar) foreach (_.setTitle(getString(R.string.animationsPrefTitle)))\n    addPreferencesFromResource(R.xml.preferences_animations)\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/appdrawer/AppDrawerFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.appdrawer\n\nimport android.app.Fragment\nimport android.os.Bundle\nimport android.preference.ListPreference\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.preferences.commons._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.Contexts\n\nclass AppDrawerFragment\n    extends PreferenceChangeListenerFragment\n    with Contexts[Fragment]\n    with FindPreferences {\n\n  lazy val dom = AppDrawerDOM(this)\n\n  lazy val appDrawerJobs = new AppDrawerJobs(new AppDrawerUiActions(dom))\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n    Option(getActivity.getActionBar) foreach (_.setTitle(getString(R.string.appDrawerPrefTitle)))\n    addPreferencesFromResource(R.xml.preferences_app_drawer)\n  }\n\n  override def onStart(): Unit = {\n    super.onStart()\n    appDrawerJobs.initialize().resolveAsync()\n  }\n\n}\n\ncase class AppDrawerDOM(dom: FindPreferences) {\n\n  def longPressPreference = dom.find[ListPreference](AppDrawerLongPressAction)\n  def animationPreference = dom.find[ListPreference](AppDrawerAnimation)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/appdrawer/AppDrawerJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.appdrawer\n\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.commons.services.TaskService.TaskService\nimport macroid.ContextWrapper\n\nclass AppDrawerJobs(ui: AppDrawerUiActions)(implicit contextWrapper: ContextWrapper) extends Jobs {\n\n  def initialize(): TaskService[Unit] = ui.initialize()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/appdrawer/AppDrawerUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.appdrawer\n\nimport android.preference.Preference\nimport android.preference.Preference.OnPreferenceChangeListener\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport macroid.extras.ResourcesExtras._\nimport cards.nine.app.ui.preferences.commons._\nimport cards.nine.commons.services.TaskService.TaskService\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.{ContextWrapper, Ui}\n\nclass AppDrawerUiActions(dom: AppDrawerDOM)(implicit contextWrapper: ContextWrapper) {\n\n  def initialize(): TaskService[Unit] =\n    Ui {\n      reloadLongPressActionText(AppDrawerLongPressAction.readValue.value)\n      reloadAnimationText(AppDrawerAnimation.readValue.value)\n      dom.longPressPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener {\n        override def onPreferenceChange(preference: Preference, newValue: scala.Any): Boolean = {\n          reloadLongPressActionText(newValue.toString)\n          true\n        }\n      })\n\n      dom.animationPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener {\n        override def onPreferenceChange(preference: Preference, newValue: scala.Any): Boolean = {\n          reloadAnimationText(newValue.toString)\n          true\n        }\n      })\n    }.toService()\n\n  private[this] def reloadLongPressActionText(value: String) = {\n    val textValue = AppDrawerLongPressActionValue(value) match {\n      case AppDrawerLongPressActionOpenKeyboard =>\n        resGetString(R.string.appDrawerOpenKeyboard)\n      case AppDrawerLongPressActionOpenContacts =>\n        resGetString(R.string.appDrawerOpenContacts)\n    }\n    dom.longPressPreference.setSummary(resGetString(R.string.appDrawerLongPressSummary, textValue))\n  }\n\n  private[this] def reloadAnimationText(value: String) = {\n    val textValue = AppDrawerAnimationValue(value) match {\n      case AppDrawerAnimationCircle =>\n        resGetString(R.string.appDrawerOpenAnimationReveal)\n      case AppDrawerAnimationFade =>\n        resGetString(R.string.appDrawerOpenAnimationFade)\n    }\n    dom.animationPreference.setSummary(\n      resGetString(R.string.appDrawerOpenAnimationSummary, textValue))\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/commons/FindPreferences.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.commons\n\nimport android.preference.PreferenceFragment\n\ntrait FindPreferences { self: PreferenceFragment =>\n\n  def find[T](pref: NineCardsPreferenceValue[_]): T =\n    findPreference(pref.name).asInstanceOf[T]\n\n  def findByName[T](name: String): T = findPreference(name).asInstanceOf[T]\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/commons/NineCardsPreferences.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.commons\n\nimport NineCardsPreferencesValue._\nimport android.content.{Context, SharedPreferences}\nimport android.preference.PreferenceManager\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nsealed trait NineCardsPreferences {\n  val name: String\n}\n\ncase object LookFeelPreferences extends NineCardsPreferences {\n  override val name: String = \"lookFeelKey\"\n}\n\ncase object MomentsPreferences extends NineCardsPreferences {\n  override val name: String = \"momentKey\"\n}\n\ncase object AppDrawerPreferences extends NineCardsPreferences {\n  override val name: String = \"appDrawerKey\"\n}\n\ncase object SizesPreferences extends NineCardsPreferences {\n  override val name: String = \"sizesKey\"\n}\n\ncase object AnimationsPreferences extends NineCardsPreferences {\n  override val name: String = \"animationsKey\"\n}\n\ncase object AnalyticsPreferences extends NineCardsPreferences {\n  override val name: String = \"analyticsKey\"\n}\n\ncase object DeveloperPreferences extends NineCardsPreferences {\n  override val name: String = \"developerKey\"\n}\n\ncase object WizardInlinePreferences extends NineCardsPreferences {\n  override val name: String = \"wizardInlineKey\"\n}\n\ncase object FeedbackPreferences extends NineCardsPreferences {\n  override val name: String = \"feedbackKey\"\n}\n\ncase object AboutPreferences extends NineCardsPreferences {\n  override val name: String = \"aboutKey\"\n}\n\ncase object HelpPreferences extends NineCardsPreferences {\n  override val name: String = \"helpKey\"\n}\n\nsealed trait NineCardsPreferenceValue[T] extends NineCardsPreferences {\n\n  val name: String\n  val default: T\n\n  def readValue(implicit contextWrapper: ContextWrapper): T =\n    readValueWith(contextWrapper.application)\n\n  def readValueWith(context: Context): T\n}\n\n// Moments Preferences\n\ncase object ShowMicSearchMoment extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"showMicSearchMoment\"\n  override val default: Boolean = false\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\ncase object ShowWeatherMoment extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"showWeatherMoment\"\n  override val default: Boolean = true\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\n// Animations Preferences\n\ncase object SpeedAnimations extends NineCardsPreferenceValue[SpeedAnimationValue] {\n  override val name: String                 = \"speed\"\n  override val default: SpeedAnimationValue = NormalAnimation\n\n  override def readValueWith(context: Context): SpeedAnimationValue =\n    SpeedAnimationValue(getString(context, name, default.value))\n\n  def getDuration(implicit contextWrapper: ContextWrapper): Int = {\n    resGetInteger(readValue match {\n      case NormalAnimation => R.integer.anim_duration_normal\n      case SlowAnimation   => R.integer.anim_duration_slow\n      case FastAnimation   => R.integer.anim_duration_fast\n    })\n  }\n}\n\ncase object CollectionOpeningAnimations extends NineCardsPreferenceValue[CollectionOpeningValue] {\n  override val name: String = \"collectionOpening\"\n  override val default: CollectionOpeningValue =\n    CircleOpeningCollectionAnimation\n\n  override def readValueWith(context: Context): CollectionOpeningValue =\n    CollectionOpeningValue(getString(context, name, default.value))\n}\n\ncase object WorkspaceAnimations extends NineCardsPreferenceValue[WorkspaceAnimationValue] {\n  override val name: String = \"workspaceAnimation\"\n  override val default: WorkspaceAnimationValue =\n    HorizontalSlideWorkspaceAnimation\n\n  override def readValueWith(context: Context): WorkspaceAnimationValue =\n    WorkspaceAnimationValue(getString(context, name, default.value))\n}\n\ncase object WallpaperAnimation extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"wallpaperAnimation\"\n  override val default: Boolean = true\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\n// App Drawer Preferences\n\ncase object AppDrawerLongPressAction\n    extends NineCardsPreferenceValue[AppDrawerLongPressActionValue] {\n  override val name: String = \"appDrawerLongPressAction\"\n  override val default: AppDrawerLongPressActionValue =\n    AppDrawerLongPressActionOpenKeyboard\n\n  override def readValueWith(context: Context): AppDrawerLongPressActionValue =\n    AppDrawerLongPressActionValue(getString(context, name, default.value))\n}\n\ncase object AppDrawerAnimation extends NineCardsPreferenceValue[AppDrawerAnimationValue] {\n  override val name: String                     = \"appDrawerAnimation\"\n  override val default: AppDrawerAnimationValue = AppDrawerAnimationCircle\n\n  override def readValueWith(context: Context): AppDrawerAnimationValue =\n    AppDrawerAnimationValue(getString(context, name, default.value))\n}\n\ncase object AppDrawerFavoriteContactsFirst extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"appDrawerFavoriteContacts\"\n  override val default: Boolean = false\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\ncase object AppDrawerSelectItemsInScroller extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"appDrawerSelectItemsInScroller\"\n  override val default: Boolean = true\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\n// Look & Feel Preferences\n\ncase object Theme extends NineCardsPreferenceValue[ThemeValue] {\n  override val name: String        = \"theme\"\n  override val default: ThemeValue = ThemeLight\n\n  override def readValueWith(context: Context): ThemeValue =\n    ThemeValue(getString(context, name, default.value))\n\n  def getThemeFile(implicit contextWrapper: ContextWrapper): String =\n    Theme.readValue match {\n      case ThemeLight => \"theme_light\"\n      case ThemeDark  => \"theme_dark\"\n    }\n}\n\ncase object GoogleLogo extends NineCardsPreferenceValue[GoogleLogoValue] {\n  override val name: String             = \"googleLogo\"\n  override val default: GoogleLogoValue = GoogleLogoTheme\n\n  override def readValueWith(context: Context): GoogleLogoValue =\n    GoogleLogoValue(getString(context, name, default.value))\n}\n\ncase object FontSize extends NineCardsPreferenceValue[FontSizeValue] {\n  override val name: String           = \"fontsSize\"\n  override val default: FontSizeValue = FontSizeMedium\n\n  override def readValueWith(context: Context): FontSizeValue =\n    FontSizeValue(getString(context, name, default.value))\n\n  def getSizeResource(implicit contextWrapper: ContextWrapper): Int = {\n    readValue match {\n      case FontSizeSmall  => R.dimen.text_medium\n      case FontSizeMedium => R.dimen.text_default\n      case FontSizeLarge  => R.dimen.text_large\n    }\n  }\n\n  def getTitleSizeResource(implicit contextWrapper: ContextWrapper): Int = {\n    readValue match {\n      case FontSizeSmall  => R.dimen.text_large\n      case FontSizeMedium => R.dimen.text_xlarge\n      case FontSizeLarge  => R.dimen.text_xxlarge\n    }\n  }\n\n  def getContactSizeResource(implicit contextWrapper: ContextWrapper): Int = {\n    readValue match {\n      case FontSizeSmall  => R.dimen.text_default\n      case FontSizeMedium => R.dimen.text_large\n      case FontSizeLarge  => R.dimen.text_xlarge\n    }\n  }\n\n}\n\ncase object IconsSize extends NineCardsPreferenceValue[IconsSizeValue] {\n  override val name: String            = \"iconsSize\"\n  override val default: IconsSizeValue = IconsSizeMedium\n\n  override def readValueWith(context: Context): IconsSizeValue =\n    IconsSizeValue(getString(context, name, default.value))\n\n  def getIconApp(implicit contextWrapper: ContextWrapper): Int = {\n    resGetDimensionPixelSize(readValue match {\n      case IconsSizeSmall  => R.dimen.size_icon_app_small\n      case IconsSizeMedium => R.dimen.size_icon_app_medium\n      case IconsSizeLarge  => R.dimen.size_icon_app_large\n    })\n  }\n\n  def getIconCollection(implicit contextWrapper: ContextWrapper): Int = {\n    resGetDimensionPixelSize(readValue match {\n      case IconsSizeSmall  => R.dimen.size_group_collection_small\n      case IconsSizeMedium => R.dimen.size_group_collection_medium\n      case IconsSizeLarge  => R.dimen.size_group_collection_large\n    })\n  }\n\n}\n\ncase object CardPadding extends NineCardsPreferenceValue[IconsSizeValue] {\n  override val name: String            = \"cardPadding\"\n  override val default: IconsSizeValue = IconsSizeMedium\n\n  override def readValueWith(context: Context): IconsSizeValue =\n    IconsSizeValue(getString(context, name, default.value))\n\n  def getPadding(implicit contextWrapper: ContextWrapper): Int = {\n    resGetDimensionPixelSize(readValue match {\n      case IconsSizeSmall  => R.dimen.card_padding_small\n      case IconsSizeMedium => R.dimen.card_padding_medium\n      case IconsSizeLarge  => R.dimen.card_padding_large\n    })\n  }\n}\n\n// Analytics\n\ncase object AnalyticsEnabled extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"analyticsEnabled\"\n  override val default: Boolean = true\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\n// Developer Preferences\n\ncase object IsDeveloper extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"isDeveloper\"\n  override val default: Boolean = false\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n\n  def convertToDeveloper(implicit contextWrapper: ContextWrapper): Unit =\n    setBoolean(contextWrapper.application, name, value = true)\n}\n\ncase object AppsCategorized extends NineCardsPreferenceValue[String] {\n  override val name: String    = \"appsCategorized\"\n  override val default: String = \"\"\n\n  override def readValueWith(context: Context): String =\n    getString(context, name, default)\n}\n\ncase object AndroidToken extends NineCardsPreferenceValue[String] {\n  override val name: String    = \"androidToken\"\n  override val default: String = \"\"\n\n  override def readValueWith(context: Context): String =\n    getString(context, name, default)\n}\n\ncase object DeviceCloudId extends NineCardsPreferenceValue[String] {\n  override val name: String    = \"deviceCloudId\"\n  override val default: String = \"\"\n\n  override def readValueWith(context: Context): String =\n    getString(context, name, default)\n}\n\ncase object CurrentDensity extends NineCardsPreferenceValue[String] {\n  override val name: String    = \"currentDensity\"\n  override val default: String = \"\"\n\n  override def readValueWith(context: Context): String =\n    getString(context, name, default)\n}\n\ncase object ProbablyActivity extends NineCardsPreferenceValue[String] {\n  override val name: String    = \"probablyActivity\"\n  override val default: String = \"\"\n\n  override def readValueWith(context: Context): String =\n    getString(context, name, default)\n}\n\ncase object Headphones extends NineCardsPreferenceValue[String] {\n  override val name: String    = \"headphones\"\n  override val default: String = \"\"\n\n  override def readValueWith(context: Context): String =\n    getString(context, name, default)\n}\n\ncase object Location extends NineCardsPreferenceValue[String] {\n  override val name: String    = \"location\"\n  override val default: String = \"\"\n\n  override def readValueWith(context: Context): String =\n    getString(context, name, default)\n}\n\ncase object Weather extends NineCardsPreferenceValue[String] {\n  override val name: String    = \"weather\"\n  override val default: String = \"\"\n\n  override def readValueWith(context: Context): String =\n    getString(context, name, default)\n}\n\ncase object ClearCacheImages extends NineCardsPreferenceValue[String] {\n  override val name: String    = \"clearCacheImages\"\n  override val default: String = \"\"\n\n  override def readValueWith(context: Context): String =\n    getString(context, name, default)\n}\n\ncase object ShowPositionInCards extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"showPositionInCards\"\n  override val default: Boolean = false\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\ncase object ShowPrintInfoOptionInAccounts extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"showPrintInfoOptionInAccounts\"\n  override val default: Boolean = false\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\ncase object OverrideBackendV2Url extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"overrideBackendV2Url\"\n  override val default: Boolean = false\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\ncase object BackendV2Url extends NineCardsPreferenceValue[String] {\n  override val name: String    = \"backendV2Url\"\n  override val default: String = \"\"\n\n  override def readValueWith(context: Context): String =\n    getString(context, name, default)\n}\n\ncase object V1EmptyDeviceWizard extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"v1EmptyDeviceWizard\"\n  override val default: Boolean = false\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\ncase object GoogleDriveEmptyDeviceWizard extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"googleDriveEmptyDeviceWizard\"\n  override val default: Boolean = false\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\ncase object IsStethoActive extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"isStethoActive\"\n  override val default: Boolean = false\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\ncase object IsFlowUpActive extends NineCardsPreferenceValue[Boolean] {\n  override val name: String     = \"isFlowUpActive\"\n  override val default: Boolean = false\n\n  override def readValueWith(context: Context): Boolean =\n    getBoolean(context, name, default)\n}\n\ncase object RestartApplication extends NineCardsPreferenceValue[String] {\n  override val name: String    = \"restartApplication\"\n  override val default: String = \"\"\n\n  override def readValueWith(context: Context): String =\n    getString(context, name, default)\n}\n\n// Commons\n\nobject NineCardsPreferencesValue {\n\n  private[this] def get[T](context: Context, f: (SharedPreferences) => T) =\n    f(PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext))\n\n  def getInt(context: Context, name: String, defaultValue: Int): Int =\n    get(context, _.getInt(name, defaultValue))\n\n  def setInt(context: Context, name: String, value: Int): Unit =\n    get(context, _.edit().putInt(name, value).apply())\n\n  def getString(context: Context, name: String, defaultValue: String): String =\n    get(context, _.getString(name, defaultValue))\n\n  def setString(context: Context, name: String, value: String): Unit =\n    get(context, _.edit().putString(name, value).apply())\n\n  def getBoolean(context: Context, name: String, defaultValue: Boolean): Boolean =\n    get(context, _.getBoolean(name, defaultValue))\n\n  def setBoolean(context: Context, name: String, value: Boolean): Unit =\n    get(context, _.edit().putBoolean(name, value).apply())\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/commons/NineCardsPreferencesValues.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.commons\n\nimport macroid.extras.DeviceVersion.Lollipop\n\n// Animations Values\n\nsealed trait SpeedAnimationValue {\n  val value: String\n}\n\ncase object SlowAnimation extends SpeedAnimationValue {\n  override val value: String = \"0\"\n}\n\ncase object NormalAnimation extends SpeedAnimationValue {\n  override val value: String = \"1\"\n}\n\ncase object FastAnimation extends SpeedAnimationValue {\n  override val value: String = \"2\"\n}\n\nobject SpeedAnimationValue {\n\n  val values = Seq(SlowAnimation, NormalAnimation, FastAnimation)\n\n  def apply(value: String): SpeedAnimationValue =\n    values find (_.value == value) getOrElse NormalAnimation\n\n}\n\nsealed trait CollectionOpeningValue {\n  val value: String\n  val isSupported: Boolean\n}\n\ncase object CircleOpeningCollectionAnimation extends CollectionOpeningValue {\n  override val value: String        = \"0\"\n  override val isSupported: Boolean = Lollipop.ifSupportedThen(()).isDefined\n}\n\ncase object NoAnimationOpeningCollectionAnimation extends CollectionOpeningValue {\n  override val value: String        = \"1\"\n  override val isSupported: Boolean = true\n}\n\nobject CollectionOpeningValue {\n\n  val values = Seq(CircleOpeningCollectionAnimation, NoAnimationOpeningCollectionAnimation)\n\n  def apply(value: String): CollectionOpeningValue =\n    values find (_.value == value) getOrElse CircleOpeningCollectionAnimation\n\n}\n\nsealed trait WorkspaceAnimationValue {\n  val value: String\n}\n\ncase object HorizontalSlideWorkspaceAnimation extends WorkspaceAnimationValue {\n  override val value: String = \"0\"\n}\n\ncase object AppearBehindWorkspaceAnimation extends WorkspaceAnimationValue {\n  override val value: String = \"1\"\n}\n\nobject WorkspaceAnimationValue {\n\n  val values =\n    Seq(HorizontalSlideWorkspaceAnimation, AppearBehindWorkspaceAnimation)\n\n  def apply(value: String): WorkspaceAnimationValue =\n    values find (_.value == value) getOrElse HorizontalSlideWorkspaceAnimation\n\n}\n\n// App Drawer Values\n\nsealed trait AppDrawerLongPressActionValue {\n  val value: String\n}\n\ncase object AppDrawerLongPressActionOpenKeyboard extends AppDrawerLongPressActionValue {\n  override val value: String = \"0\"\n}\n\ncase object AppDrawerLongPressActionOpenContacts extends AppDrawerLongPressActionValue {\n  override val value: String = \"1\"\n}\n\nobject AppDrawerLongPressActionValue {\n\n  val values = Seq(AppDrawerLongPressActionOpenKeyboard, AppDrawerLongPressActionOpenContacts)\n\n  def apply(value: String): AppDrawerLongPressActionValue =\n    values find (_.value == value) getOrElse AppDrawerLongPressActionOpenKeyboard\n\n}\n\nsealed trait AppDrawerAnimationValue {\n  val value: String\n  val isSupported: Boolean\n}\n\ncase object AppDrawerAnimationCircle extends AppDrawerAnimationValue {\n  override val value: String        = \"0\"\n  override val isSupported: Boolean = Lollipop.ifSupportedThen(()).isDefined\n}\n\ncase object AppDrawerAnimationFade extends AppDrawerAnimationValue {\n  override val value: String        = \"1\"\n  override val isSupported: Boolean = true\n}\n\nobject AppDrawerAnimationValue {\n\n  val values = Seq(AppDrawerAnimationCircle, AppDrawerAnimationFade)\n\n  def apply(value: String): AppDrawerAnimationValue =\n    values find (_.value == value) getOrElse AppDrawerAnimationCircle\n\n}\n\n// Look and Feel Values\n\nsealed trait ThemeValue {\n  val value: String\n}\n\ncase object ThemeDark extends ThemeValue {\n  override val value: String = \"0\"\n}\n\ncase object ThemeLight extends ThemeValue {\n  override val value: String = \"1\"\n}\n\nobject ThemeValue {\n\n  val values = Seq(ThemeDark, ThemeLight)\n\n  def apply(value: String): ThemeValue =\n    values find (_.value == value) getOrElse ThemeLight\n\n}\n\nsealed trait GoogleLogoValue {\n  val value: String\n}\n\ncase object GoogleLogoTheme extends GoogleLogoValue {\n  override val value: String = \"0\"\n}\n\ncase object GoogleLogoColoured extends GoogleLogoValue {\n  override val value: String = \"1\"\n}\n\nobject GoogleLogoValue {\n\n  val values = Seq(GoogleLogoTheme, GoogleLogoColoured)\n\n  def apply(value: String): GoogleLogoValue =\n    values find (_.value == value) getOrElse GoogleLogoTheme\n\n}\n\nsealed trait FontSizeValue {\n  val value: String\n}\n\ncase object FontSizeSmall extends FontSizeValue {\n  override val value: String = \"0\"\n}\n\ncase object FontSizeMedium extends FontSizeValue {\n  override val value: String = \"1\"\n}\n\ncase object FontSizeLarge extends FontSizeValue {\n  override val value: String = \"2\"\n}\n\nobject FontSizeValue {\n\n  val values = Seq(FontSizeSmall, FontSizeMedium, FontSizeLarge)\n\n  def apply(value: String): FontSizeValue =\n    values find (_.value == value) getOrElse FontSizeMedium\n\n}\n\nsealed trait IconsSizeValue {\n  val value: String\n}\n\ncase object IconsSizeSmall extends IconsSizeValue {\n  override val value: String = \"0\"\n}\n\ncase object IconsSizeMedium extends IconsSizeValue {\n  override val value: String = \"1\"\n}\n\ncase object IconsSizeLarge extends IconsSizeValue {\n  override val value: String = \"2\"\n}\n\nobject IconsSizeValue {\n\n  val values = Seq(IconsSizeSmall, IconsSizeMedium, IconsSizeLarge)\n\n  def apply(value: String): IconsSizeValue =\n    values find (_.value == value) getOrElse IconsSizeMedium\n\n}\n\nsealed trait CardPaddingValue {\n  val value: String\n}\n\ncase object CardPaddingSmall extends CardPaddingValue {\n  override val value: String = \"0\"\n}\n\ncase object CardPaddingMedium extends CardPaddingValue {\n  override val value: String = \"1\"\n}\n\ncase object CardPaddingLarge extends CardPaddingValue {\n  override val value: String = \"2\"\n}\n\nobject CardPaddingValue {\n\n  val values = Seq(CardPaddingSmall, CardPaddingMedium, CardPaddingLarge)\n\n  def apply(value: String): CardPaddingValue =\n    values find (_.value == value) getOrElse CardPaddingMedium\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/commons/PreferenceChangeListenerFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.commons\n\nimport android.content.SharedPreferences\nimport android.content.SharedPreferences.OnSharedPreferenceChangeListener\nimport android.preference.PreferenceFragment\nimport cards.nine.app.ui.preferences.NineCardsPreferencesActivity\n\ntrait PreferenceChangeListenerFragment\n    extends PreferenceFragment\n    with OnSharedPreferenceChangeListener {\n\n  override def onResume(): Unit = {\n    super.onResume()\n    getPreferenceManager.getSharedPreferences.registerOnSharedPreferenceChangeListener(this)\n  }\n\n  override def onPause(): Unit = {\n    getPreferenceManager.getSharedPreferences.unregisterOnSharedPreferenceChangeListener(this)\n    super.onPause()\n  }\n\n  override def onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String): Unit =\n    withActivity(_.preferenceChanged(key))\n\n  protected def withActivity[T](f: (NineCardsPreferencesActivity) => T): Option[T] =\n    Option(getActivity) match {\n      case Some(a: NineCardsPreferencesActivity) => Some(f(a))\n      case _                                     => None\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/developers/AppsListFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.developers\n\nimport android.app.Fragment\nimport android.os.Bundle\nimport android.preference.{PreferenceCategory, PreferenceFragment}\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.preferences.commons._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.Contexts\n\nclass AppsListFragment extends PreferenceFragment with Contexts[Fragment] with FindPreferences {\n\n  lazy val dom = AppsListDOM(this)\n\n  lazy val appsListJobs = new AppsListJobs(new AppsListUiActions(dom))\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n    Option(getActivity.getActionBar) foreach (_.setTitle(getString(R.string.devAppsListTitle)))\n    addPreferencesFromResource(R.xml.preferences_apps_list)\n\n    appsListJobs.initialize().resolveAsync()\n  }\n\n}\n\ncase class AppsListDOM(dom: FindPreferences) {\n\n  def appsListPreferenceCategory =\n    dom.findByName[PreferenceCategory](\"appsList\")\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/developers/AppsListJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.developers\n\nimport cards.nine.app.ui.commons.{ImplicitsUiExceptions, Jobs}\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.GetByName\nimport macroid.ContextWrapper\n\nclass AppsListJobs(ui: AppsListUiActions)(implicit contextWrapper: ContextWrapper)\n    extends Jobs\n    with ImplicitsUiExceptions {\n\n  def initialize() =\n    for {\n      apps <- di.deviceProcess.getSavedApps(GetByName)\n      _    <- ui.loadApps(apps)\n    } yield ()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/developers/AppsListUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.developers\n\nimport android.graphics.drawable.BitmapDrawable\nimport android.preference.Preference\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.ops.NineCardsCategoryOps._\nimport cards.nine.models.ApplicationData\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\n\nimport scala.util.{Success, Try}\n\nclass AppsListUiActions(dom: AppsListDOM)(implicit contextWrapper: ContextWrapper) {\n\n  def loadApps(apps: Seq[ApplicationData]): TaskService[Unit] =\n    Ui {\n      val packageManager = contextWrapper.bestAvailable.getPackageManager\n      apps foreach { app =>\n        val preference = new Preference(contextWrapper.bestAvailable)\n        preference.setTitle(app.name)\n        preference.setSummary(s\"${app.category.getName} (${app.packageName})\")\n        Try {\n          packageManager.getApplicationIcon(app.packageName).asInstanceOf[BitmapDrawable]\n        } match {\n          case Success(drawable) => preference.setIcon(drawable)\n          case _                 => preference.setIcon(R.drawable.ic_launcher)\n        }\n        dom.appsListPreferenceCategory.addPreference(preference)\n      }\n    }.toService()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/developers/DeveloperFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.developers\n\nimport android.app.Fragment\nimport android.os.Bundle\nimport android.preference.Preference.OnPreferenceClickListener\nimport android.preference.{CheckBoxPreference, Preference, PreferenceFragment}\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.preferences.commons._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.Contexts\n\nclass DeveloperFragment extends PreferenceFragment with Contexts[Fragment] with FindPreferences {\n\n  lazy val dom = DeveloperDOM(this)\n\n  lazy val preferencesJobs = new DeveloperJobs(new DeveloperUiActions(dom))\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n    Option(getActivity.getActionBar) foreach (_.setTitle(getString(R.string.developerPrefTitle)))\n    addPreferencesFromResource(R.xml.preferences_dev)\n\n    preferencesJobs.initialize().resolveAsync()\n\n    dom.appsCategorizedPreferences.setOnPreferenceClickListener(new OnPreferenceClickListener {\n      override def onPreferenceClick(preference: Preference): Boolean = {\n        getFragmentManager\n          .beginTransaction()\n          .addToBackStack(AppsCategorized.name)\n          .replace(android.R.id.content, new AppsListFragment)\n          .commit()\n        true\n      }\n    })\n\n  }\n\n}\n\ncase class DeveloperDOM(dom: FindPreferences) {\n\n  def appsCategorizedPreferences = dom.find[Preference](AppsCategorized)\n  def overrideBackendV2UrlPreference =\n    dom.find[CheckBoxPreference](OverrideBackendV2Url)\n  def backendV2UrlPreference       = dom.find[Preference](BackendV2Url)\n  def androidTokenPreferences      = dom.find[Preference](AndroidToken)\n  def deviceCloudIdPreferences     = dom.find[Preference](DeviceCloudId)\n  def currentDensityPreferences    = dom.find[Preference](CurrentDensity)\n  def probablyActivityPreference   = dom.find[Preference](ProbablyActivity)\n  def headphonesPreference         = dom.find[Preference](Headphones)\n  def locationPreference           = dom.find[Preference](Location)\n  def weatherPreference            = dom.find[Preference](Weather)\n  def restartApplicationPreference = dom.find[Preference](RestartApplication)\n  def clearCacheImagesPreference   = dom.find[Preference](ClearCacheImages)\n  def isStethoActivePreference     = dom.find[CheckBoxPreference](IsStethoActive)\n  def isFlowUpActivePreference     = dom.find[CheckBoxPreference](IsFlowUpActive)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/developers/DeveloperJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.developers\n\nimport android.app.Activity\nimport android.content.Intent\nimport cards.nine.app.ui.commons.{ImplicitsUiExceptions, JobException, Jobs, UiException}\nimport cards.nine.app.ui.launcher.LauncherActivity\nimport cards.nine.app.ui.preferences.commons.{\n  BackendV2Url,\n  IsFlowUpActive,\n  IsStethoActive,\n  OverrideBackendV2Url\n}\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.GetByName\nimport cats.implicits._\nimport com.bumptech.glide.Glide\nimport macroid.ContextWrapper\n\nclass DeveloperJobs(ui: DeveloperUiActions)(implicit contextWrapper: ContextWrapper)\n    extends Jobs\n    with ImplicitsUiExceptions {\n\n  def initialize() =\n    (ui.initialize(this) |@|\n      loadAppsCategorized |@|\n      loadBackendV2Status |@|\n      loadMostProbableActivity |@|\n      loadHeadphone |@|\n      loadLocation |@|\n      loadWeather |@|\n      loadStethoStatus |@|\n      loadFlowUpStatus).tupled\n\n  def loadAppsCategorized: TaskService[Unit] =\n    for {\n      apps <- di.deviceProcess.getSavedApps(GetByName)\n      _    <- ui.setAppsCategorizedSummary(apps)\n    } yield ()\n\n  def loadBackendV2Status: TaskService[Unit] =\n    for {\n      _ <- ui.enableBackendV2Url(OverrideBackendV2Url.readValue)\n      _ <- ui.setBackendV2UrlSummary(BackendV2Url.readValue)\n    } yield ()\n\n  def loadStethoStatus: TaskService[Unit] =\n    ui.setStethoTitle(IsStethoActive.readValue)\n\n  def loadFlowUpStatus: TaskService[Unit] =\n    ui.setFlowUpTitle(IsFlowUpActive.readValue)\n\n  def copyAndroidToken: TaskService[Unit] =\n    for {\n      user <- di.userProcess.getUser\n      _    <- ui.copyToClipboard(user.deviceToken)\n    } yield ()\n\n  def copyDeviceCloudId: TaskService[Unit] =\n    for {\n      user <- di.userProcess.getUser\n      _    <- ui.copyToClipboard(user.deviceCloudId)\n    } yield ()\n\n  def clearCacheImages: TaskService[Unit] = {\n    val clearCacheService = TaskService {\n      CatchAll[UiException] {\n        Glide.get(contextWrapper.bestAvailable).clearDiskCache()\n      }\n    }\n    clearCacheService *> ui.cacheCleared\n  }\n\n  def restartApplication: TaskService[Unit] = TaskService {\n    CatchAll[JobException] {\n      val intent =\n        new Intent(contextWrapper.bestAvailable, classOf[LauncherActivity])\n      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n      contextWrapper.bestAvailable.startActivity(intent)\n      contextWrapper.original.get match {\n        case Some(a: Activity) => a.finish()\n        case _                 =>\n      }\n      Runtime.getRuntime.exit(0)\n    }\n  }\n\n  def loadMostProbableActivity: TaskService[Unit] =\n    for {\n      probableActivity <- di.recognitionProcess.getMostProbableActivity\n      _                <- ui.setProbablyActivitySummary(probableActivity.activityType.toString)\n    } yield ()\n\n  def loadHeadphone: TaskService[Unit] =\n    for {\n      headphone <- di.recognitionProcess.getHeadphone\n      _         <- ui.setHeadphonesSummary(headphone.connected)\n    } yield ()\n\n  def loadLocation: TaskService[Unit] =\n    for {\n      location <- di.recognitionProcess.getLocation\n      _        <- ui.setLocationSummary(location)\n    } yield ()\n\n  def loadWeather: TaskService[Unit] =\n    for {\n      weather <- di.recognitionProcess.getWeather\n      _       <- ui.setWeatherSummary(weather)\n    } yield ()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/developers/DeveloperUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.developers\n\nimport android.content.{ClipData, ClipboardManager, Context}\nimport android.preference.Preference\nimport android.preference.Preference.{OnPreferenceChangeListener, OnPreferenceClickListener}\nimport macroid.extras.UIActionsExtras._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.Misc\nimport cards.nine.models.{ApplicationData, Location, WeatherState}\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.{ContextWrapper, Ui}\n\nclass DeveloperUiActions(dom: DeveloperDOM)(implicit contextWrapper: ContextWrapper) {\n\n  def initialize(developerJobs: DeveloperJobs): TaskService[Unit] = {\n    def clickPreference(onClick: () => Unit) = new OnPreferenceClickListener {\n      override def onPreferenceClick(preference: Preference): Boolean = {\n        onClick()\n        true\n      }\n    }\n\n    def changePreference(onChange: (Preference, scala.Any) => Unit) =\n      new OnPreferenceChangeListener {\n        override def onPreferenceChange(preference: Preference, newValue: scala.Any): Boolean = {\n          onChange(preference, newValue)\n          true\n        }\n      }\n\n    val density =\n      contextWrapper.bestAvailable.getResources.getDisplayMetrics.density\n    val densityString = density match {\n      case d if d <= 0.75 => \"LDPI\"\n      case d if d <= 1.0  => \"MDPI\"\n      case d if d <= 1.5  => \"HDPI\"\n      case d if d <= 2.0  => \"XHDPI\"\n      case d if d <= 3.0  => \"XXHDPI\"\n      case d if d <= 4.0  => \"XXXHDPI\"\n      case d              => d.toString\n    }\n\n    val densityDpi =\n      contextWrapper.bestAvailable.getResources.getDisplayMetrics.densityDpi\n\n    dom.currentDensityPreferences.setSummary(s\"$densityString ($density) - $densityDpi dp\")\n\n    Ui {\n      dom.backendV2UrlPreference.setOnPreferenceChangeListener(changePreference((p, v) => {\n        p.setSummary(v.toString)\n      }))\n      dom.overrideBackendV2UrlPreference.setOnPreferenceChangeListener(changePreference((p, v) => {\n        enableBackendV2Url(v.asInstanceOf[Boolean]).resolveAsync()\n      }))\n      dom.isStethoActivePreference.setOnPreferenceChangeListener(changePreference((p, v) => {\n        setStethoTitle(v.asInstanceOf[Boolean]).resolveAsync()\n      }))\n      dom.isFlowUpActivePreference.setOnPreferenceChangeListener(changePreference((p, v) => {\n        setFlowUpTitle(v.asInstanceOf[Boolean]).resolveAsync()\n      }))\n      dom.androidTokenPreferences.setOnPreferenceClickListener(clickPreference(() => {\n        developerJobs.copyAndroidToken.resolveAsync()\n      }))\n      dom.deviceCloudIdPreferences.setOnPreferenceClickListener(clickPreference(() => {\n        developerJobs.copyDeviceCloudId.resolveAsync()\n      }))\n      dom.probablyActivityPreference.setOnPreferenceClickListener(clickPreference(() => {\n        dom.probablyActivityPreference.setSummary(\"\")\n        developerJobs.loadMostProbableActivity.resolveAsync()\n      }))\n      dom.headphonesPreference.setOnPreferenceClickListener(clickPreference(() => {\n        dom.headphonesPreference.setSummary(\"\")\n        developerJobs.loadHeadphone.resolveAsync()\n      }))\n      dom.locationPreference.setOnPreferenceClickListener(clickPreference(() => {\n        dom.locationPreference.setSummary(\"\")\n        developerJobs.loadLocation.resolveAsync()\n      }))\n      dom.weatherPreference.setOnPreferenceClickListener(clickPreference(() => {\n        dom.weatherPreference.setSummary(\"\")\n        developerJobs.loadWeather.resolveAsync()\n      }))\n      dom.clearCacheImagesPreference.setOnPreferenceClickListener(clickPreference(() => {\n        developerJobs.clearCacheImages.resolveAsync()\n      }))\n      dom.restartApplicationPreference.setOnPreferenceClickListener(clickPreference(() => {\n        developerJobs.restartApplication.resolveAsync()\n      }))\n    }.toService()\n  }\n\n  def copyToClipboard(maybeText: Option[String]): TaskService[Unit] =\n    (uiShortToast(R.string.devCopiedToClipboard) ~ Ui {\n      (Option(contextWrapper.application.getSystemService(Context.CLIPBOARD_SERVICE)), maybeText) match {\n        case (Some(manager: ClipboardManager), Some(text)) =>\n          val clip = ClipData.newPlainText(text, text)\n          manager.setPrimaryClip(clip)\n        case _ =>\n      }\n    }).toService()\n\n  def cacheCleared: TaskService[Unit] =\n    uiShortToast(R.string.devCacheCleared).toService()\n\n  def setAppsCategorizedSummary(apps: Seq[ApplicationData]): TaskService[Unit] =\n    Ui {\n      val categorizedCount = apps.count(_.category != Misc)\n      val total            = apps.length\n      val summary =\n        resGetString(R.string.devAppsCategorizedSummary, categorizedCount.toString, total.toString)\n      dom.appsCategorizedPreferences.setSummary(summary)\n    }.toService()\n\n  def setProbablyActivitySummary(summary: String): TaskService[Unit] =\n    Ui {\n      dom.probablyActivityPreference.setSummary(summary)\n    }.toService()\n\n  def setHeadphonesSummary(connected: Boolean): TaskService[Unit] =\n    Ui {\n      val summary =\n        s\"Headphones ${if (connected) \"connected\" else \"disconnected\"}\"\n      dom.headphonesPreference.setSummary(summary)\n    }.toService()\n\n  def setLocationSummary(location: Location): TaskService[Unit] =\n    Ui {\n      val summary =\n        s\"\"\"${location.addressLines.mkString(\", \")}\n         |(${location.latitude}, ${location.longitude})\n         |${location.countryName.getOrElse(\"<No country>\")} (${location.countryCode.getOrElse(\"-\")})\"\"\".stripMargin\n      dom.locationPreference.setSummary(summary)\n    }.toService()\n\n  def setWeatherSummary(weather: WeatherState): TaskService[Unit] =\n    Ui {\n      val summary =\n        s\"${weather.conditions.headOption getOrElse \"No Conditions\"} Temp: ${weather.temperatureCelsius} C -  ${weather.temperatureFahrenheit} F\"\n      dom.weatherPreference.setSummary(summary)\n    }.toService()\n\n  def enableBackendV2Url(enable: Boolean): TaskService[Unit] =\n    Ui {\n      dom.backendV2UrlPreference.setEnabled(enable)\n    }.toService()\n\n  def setBackendV2UrlSummary(backendV2Url: String): TaskService[Unit] =\n    Ui {\n      dom.backendV2UrlPreference.setSummary(backendV2Url)\n    }.toService()\n\n  def setStethoTitle(enabled: Boolean): TaskService[Unit] =\n    Ui {\n      val title =\n        if (enabled) R.string.devIsStethoActiveTrue\n        else R.string.devIsStethoActiveFalse\n      dom.isStethoActivePreference.setTitle(title)\n    }.toService()\n\n  def setFlowUpTitle(enabled: Boolean): TaskService[Unit] =\n    Ui {\n      val title =\n        if (enabled) R.string.devIsFlowUpActiveTrue\n        else R.string.devIsFlowUpActiveFalse\n      dom.isFlowUpActivePreference.setTitle(title)\n    }.toService()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/lookandfeel/LookFeelFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.lookandfeel\n\nimport android.os.Bundle\nimport cards.nine.app.ui.preferences.commons.PreferenceChangeListenerFragment\nimport com.fortysevendeg.ninecardslauncher.R\n\nclass LookFeelFragment extends PreferenceChangeListenerFragment {\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n    withActivity { activity =>\n      Option(activity.getActionBar) foreach (_.setTitle(getString(R.string.lookFeelPrefTitle)))\n    }\n    addPreferencesFromResource(R.xml.preferences_lookfeel)\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/preferences/moments/MomentsFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.preferences.moments\n\nimport android.os.Bundle\nimport cards.nine.app.ui.preferences.commons.PreferenceChangeListenerFragment\nimport com.fortysevendeg.ninecardslauncher.R\n\nclass MomentsFragment extends PreferenceChangeListenerFragment {\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n    withActivity { activity =>\n      Option(activity.getActionBar) foreach (_.setTitle(getString(R.string.momentsPrefTitle)))\n    }\n    addPreferencesFromResource(R.xml.preferences_moments)\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/profile/ProfileActivity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.profile\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.support.v7.app.AppCompatActivity\nimport android.view.{Menu, MenuItem}\nimport cards.nine.app.commons.{BroadcastDispatcher, ContextSupportProvider}\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.commons.action_filters._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.profile.jobs.{ProfileDOM, ProfileJobs, ProfileListener, ProfileUiActions}\nimport cards.nine.app.ui.profile.models.{\n  AccountsTab,\n  ProfileTab,\n  PublicationsTab,\n  SubscriptionsTab\n}\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.SharedCollection\nimport cards.nine.process.cloud.CloudStorageClientListener\nimport cards.nine.process.sharedcollections.SharedCollectionsConfigurationException\nimport cats.implicits._\nimport com.fortysevendeg.ninecardslauncher.{R, TypedFindView}\nimport com.google.android.gms.common.ConnectionResult\nimport com.google.android.gms.common.api.GoogleApiClient\nimport macroid.{ActivityContextWrapper, Contexts, FragmentManagerContext}\nimport monix.execution.cancelables.SerialCancelable\n\nclass ProfileActivity\n    extends AppCompatActivity\n    with Contexts[AppCompatActivity]\n    with ContextSupportProvider\n    with TypedFindView\n    with ProfileListener\n    with CloudStorageClientListener\n    with BroadcastDispatcher {\n\n  import ProfileActivity._\n  import SyncDeviceState._\n\n  implicit lazy val uiContext: UiContext[Activity] = ActivityUiContext(this)\n\n  lazy val jobs = createProfileJobs\n\n  val actionsFilters: Seq[String] = SyncActionFilter.cases map (_.action)\n\n  override def manageCommand(action: String, data: Option[String]): Unit =\n    (SyncActionFilter(action), data) match {\n      case (SyncStateActionFilter, Some(`stateSuccess`)) =>\n        jobs\n          .accountSynced()\n          .resolveAsyncServiceOr(_ => jobs.profileUiActions.showEmptyAccountsContent(error = true))\n      case (SyncStateActionFilter, Some(`stateFailure`)) =>\n        jobs.errorSyncing().resolveAsyncServiceOr(_ => jobs.profileUiActions.showContactUsError())\n      case (SyncAnswerActionFilter, Some(`stateSyncing`)) =>\n        jobs.stateSyncing().resolveAsyncServiceOr(_ => jobs.profileUiActions.showContactUsError())\n      case _ =>\n    }\n\n  override def onCreate(bundle: Bundle) = {\n    super.onCreate(bundle)\n    setContentView(R.layout.profile_activity)\n    statuses = statuses.reset()\n    jobs\n      .initialize()\n      .resolveAsync(\n        onResult = (_) => jobs.profileUiActions.openProfileWizardInline().resolveAsync(),\n        onException = (_) => jobs.profileUiActions.showEmptyAccountsContent(error = true))\n  }\n\n  override def onResume(): Unit = {\n    super.onResume()\n    registerDispatchers()\n    jobs.resume().resolveAsync()\n  }\n\n  override def onPause(): Unit = {\n    super.onPause()\n    unregisterDispatcher()\n  }\n\n  override def onStop(): Unit = {\n    jobs.stop().resolveAsync()\n    super.onStop()\n  }\n\n  override def onDestroy(): Unit = {\n    statuses = statuses.reset()\n    super.onDestroy()\n  }\n\n  override def onCreateOptionsMenu(menu: Menu): Boolean = {\n    getMenuInflater.inflate(R.menu.profile_menu, menu)\n    super.onCreateOptionsMenu(menu)\n  }\n\n  override def onOptionsItemSelected(item: MenuItem): Boolean =\n    item.getItemId match {\n      case android.R.id.home =>\n        finish()\n        true\n      case R.id.action_logout =>\n        jobs.quit().resolveAsyncServiceOr(_ => jobs.profileUiActions.showContactUsError())\n        true\n      case _ =>\n        super.onOptionsItemSelected(item)\n    }\n\n  override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit =\n    jobs.activityResult(requestCode, resultCode, data).resolveAsync()\n\n  override def onBarLayoutOffsetChanged(maxScroll: Float, offset: Int): Unit = {\n    val percentage = Math.abs(offset) / maxScroll\n    jobs.onOffsetChanged(percentage).resolveAsync()\n  }\n\n  override def onClickProfileTab(tab: ProfileTab): Unit = loadTab(tab)\n\n  override def onClickReloadTab(tab: ProfileTab): Unit = loadTab(tab)\n\n  override def onClickSynchronizeDevice(): Unit =\n    jobs.launchService().resolveAsync()\n\n  override def onClickSubscribeCollection(\n      sharedCollectionId: String,\n      newSubscribeStatus: Boolean): Unit =\n    jobs.changeSubscriptionStatus(sharedCollectionId, newSubscribeStatus).resolveAsyncServiceOr {\n      _ =>\n        jobs.profileUiActions.showErrorSubscribing(triedToSubscribe = newSubscribeStatus) *>\n          jobs.profileUiActions.refreshCurrentSubscriptions()\n    }\n\n  override def onClickCopyDevice(cloudId: String, actualName: String): Unit =\n    jobs.profileUiActions.showDialogForCopyDevice(cloudId, actualName).resolveAsync()\n\n  override def onClickRenameDevice(cloudId: String, actualName: String): Unit =\n    jobs.profileUiActions.showDialogForRenameDevice(cloudId, actualName).resolveAsync()\n\n  override def onClickDeleteDevice(cloudId: String): Unit =\n    jobs.profileUiActions.showDialogForDeleteDevice(cloudId).resolveAsync()\n\n  override def onClickPrintInfoDevice(cloudId: String): Unit =\n    jobs\n      .printDeviceInfo(cloudId)\n      .resolveAsyncServiceOr(_ => jobs.profileUiActions.showContactUsError())\n\n  override def onClickOkRemoveDeviceDialog(cloudId: String): Unit =\n    jobs.deleteDevice(cloudId).resolveAsync(onException = _ => loadTab(AccountsTab, error = true))\n\n  override def onClickOkRenameDeviceDialog(\n      maybeName: Option[String],\n      cloudId: String,\n      actualName: String): Unit =\n    jobs\n      .renameDevice(maybeName, cloudId, actualName)\n      .resolveAsync(onException = _ => loadTab(AccountsTab, error = true))\n\n  override def onClickOkOnCopyDeviceDialog(\n      maybeName: Option[String],\n      cloudId: String,\n      actualName: String): Unit =\n    jobs\n      .copyDevice(maybeName, cloudId, actualName)\n      .resolveAsync(onException = _ => loadTab(AccountsTab, error = true))\n\n  override def onDriveConnectionSuspended(cause: Int): Unit = {}\n\n  override def onDriveConnected(): Unit =\n    jobs\n      .driveConnected()\n      .resolveAsyncServiceOr(_ => jobs.profileUiActions.showEmptyAccountsContent(error = true))\n\n  override def onDriveConnectionFailed(connectionResult: ConnectionResult): Unit =\n    jobs\n      .driveConnectionFailed(connectionResult)\n      .resolveAsyncServiceOr(_ => jobs.profileUiActions.showContactUsError())\n\n  override def onClickAddSharedCollection(collection: SharedCollection): Unit =\n    jobs\n      .saveSharedCollection(collection)\n      .resolveAsyncServiceOr(_ => jobs.profileUiActions.showErrorSavingCollectionInScreen())\n\n  override def onClickShareSharedCollection(collection: SharedCollection): Unit =\n    jobs\n      .shareCollection(collection)\n      .resolveAsyncServiceOr(_ => jobs.profileUiActions.showContactUsError())\n\n  private[this] def loadTab(tab: ProfileTab, error: Boolean = false): Unit = {\n\n    def withError(service: TaskService[Unit]): TaskService[Unit] =\n      if (error) {\n        for {\n          _ <- jobs.profileUiActions.showContactUsError()\n          _ <- service\n        } yield ()\n      } else service\n\n    def onException(service: TaskService[Unit]): (Throwable) => TaskService[Unit] = {\n      case e: SharedCollectionsConfigurationException =>\n        AppLog.invalidConfigurationV2\n        service\n      case _ => service\n    }\n\n    tab match {\n      case AccountsTab =>\n        serialCancelableTaskRef := withError(jobs.loadUserAccounts())\n          .resolveAutoCancelableAsyncServiceOr(_ =>\n            jobs.profileUiActions.showEmptyAccountsContent(error = true))\n      case PublicationsTab =>\n        serialCancelableTaskRef := withError(jobs.loadPublications())\n          .resolveAutoCancelableAsyncServiceOr(\n            onException(jobs.profileUiActions.showEmptyPublicationsContent(error = true)))\n      case SubscriptionsTab =>\n        serialCancelableTaskRef := withError(jobs.loadSubscriptions())\n          .resolveAutoCancelableAsyncServiceOr(\n            onException(jobs.profileUiActions.showEmptySubscriptionsContent(error = true)))\n    }\n  }\n}\n\nobject ProfileActivity {\n\n  var statuses = ProfileStatuses()\n\n  val serialCancelableTaskRef = SerialCancelable()\n\n  def createProfileJobs(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]) = {\n    val dom = new ProfileDOM(activityContextWrapper.getOriginal)\n    val listener =\n      activityContextWrapper.getOriginal.asInstanceOf[ProfileListener]\n    new ProfileJobs(new ProfileUiActions(dom, listener))\n  }\n\n}\n\ncase class ProfileStatuses(apiClient: Option[GoogleApiClient] = None) {\n\n  def reset() = ProfileStatuses()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/profile/adapters/AccountsAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.profile.adapters\n\nimport android.support.v7.widget.RecyclerView\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.styles.CommonStyles\nimport cards.nine.app.ui.components.widgets.TintableImageView\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.app.ui.preferences.commons.ShowPrintInfoOptionInAccounts\nimport cards.nine.app.ui.profile.adapters.AccountOptions._\nimport cards.nine.app.ui.profile.models.{AccountSync, Device, Header}\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.{CardLayoutBackgroundColor, DrawerIconColor}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\ncase class AccountsAdapter(\n    items: Seq[AccountSync],\n    clickListener: (AccountOption, AccountSync) => Unit)(\n    implicit activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[ViewHolderAccountsAdapter] {\n\n  private[this] val headerType = 0\n\n  private[this] val itemType = 1\n\n  override def getItemCount: Int = items.size\n\n  override def onBindViewHolder(viewHolder: ViewHolderAccountsAdapter, position: Int): Unit =\n    viewHolder.bind(items(position), position).run\n\n  override def onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderAccountsAdapter =\n    viewType match {\n      case `headerType` =>\n        val view = LayoutInflater\n          .from(parent.getContext)\n          .inflate(R.layout.profile_account_item_header, parent, false)\n        ViewHolderAccountsHeaderAdapter(view)\n      case _ =>\n        val view = LayoutInflater\n          .from(parent.getContext)\n          .inflate(R.layout.profile_account_item, parent, false)\n        ViewHolderAccountItemAdapter(view, clickListener)\n    }\n\n  override def getItemViewType(position: Int): Int =\n    if (items(position).accountSyncType == Header) headerType else itemType\n\n}\n\nabstract class ViewHolderAccountsAdapter(\n    content: View)(implicit context: ActivityContextWrapper, theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView {\n\n  def bind(accountSync: AccountSync, position: Int)(implicit uiContext: UiContext[_]): Ui[_]\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n\n}\n\ncase class ViewHolderAccountsHeaderAdapter(\n    content: View)(implicit context: ActivityContextWrapper, val theme: NineCardsTheme)\n    extends ViewHolderAccountsAdapter(content)\n    with AccountsAdapterStyles {\n\n  lazy val title = findView(TR.title)\n\n  (title <~ subtitleTextStyle).run\n\n  def bind(accountSync: AccountSync, position: Int)(implicit uiContext: UiContext[_]): Ui[_] =\n    title <~ tvText(accountSync.title)\n\n}\n\ncase class ViewHolderAccountItemAdapter(\n    content: View,\n    onClick: (AccountOption, AccountSync) => Unit)(\n    implicit context: ActivityContextWrapper,\n    val theme: NineCardsTheme)\n    extends ViewHolderAccountsAdapter(content)\n    with AccountsAdapterStyles {\n\n  lazy val currentAccountOptions = Seq(\n    (CopyOption, resGetString(R.string.menuAccountCopy)),\n    (SyncOption, resGetString(R.string.menuAccountSync)),\n    (ChangeNameOption, resGetString(R.string.menuAccountChangeName)))\n\n  lazy val otherAccountOptions = Seq(\n    (CopyOption, resGetString(R.string.menuAccountCopy)),\n    (DeleteOption, resGetString(R.string.menuAccountDelete)),\n    (ChangeNameOption, resGetString(R.string.menuAccountChangeName)))\n\n  def menuOptions(isCurrent: Boolean): Seq[(AccountOption, String)] =\n    if (isCurrent) currentAccountOptions else otherAccountOptions\n\n  lazy val title = findView(TR.profile_account_title)\n\n  lazy val printDriveInfo =\n    (PrintInfoOption, resGetString(R.string.menuAccountPrintInfo))\n\n  lazy val showPrintDriveInfo = ShowPrintInfoOptionInAccounts.readValue\n\n  lazy val subtitle = findView(TR.profile_account_subtitle)\n\n  lazy val device = findView(TR.profile_account_device)\n\n  lazy val icon = findView(TR.profile_account_action)\n\n  ((title <~ titleTextStyle) ~\n    (subtitle <~ subtitleTextStyle) ~\n    (device <~ iconStyle) ~\n    (icon <~ iconStyle)).run\n\n  def bind(accountSync: AccountSync, position: Int)(implicit uiContext: UiContext[_]): Ui[_] = {\n\n    val isCurrent = accountSync.accountSyncType match {\n      case d: Device => d.current\n      case _         => false\n    }\n\n    val menuSeq =\n      if (showPrintDriveInfo) menuOptions(isCurrent) :+ printDriveInfo\n      else menuOptions(isCurrent)\n\n    (title <~ tvText(accountSync.title)) ~\n      (subtitle <~ tvText(accountSync.subtitle getOrElse \"\")) ~\n      (icon <~ ivSrc(R.drawable.icon_account_options) <~\n        On.click {\n          icon <~\n            vListThemedPopupWindowShow(values = menuSeq map {\n              case (_, name) => name\n            }, onItemClickListener = (position) => {\n              menuSeq.lift(position) foreach {\n                case (option, _) => onClick(option, accountSync)\n              }\n            }, width = Option(resGetDimensionPixelSize(R.dimen.width_list_popup_menu)))\n        })\n  }\n\n}\n\ntrait AccountsAdapterStyles extends CommonStyles {\n\n  def rootStyle()(implicit context: ContextWrapper, theme: NineCardsTheme): Tweak[View] =\n    vBackgroundColor(theme.get(CardLayoutBackgroundColor))\n\n  def iconStyle(\n      implicit context: ContextWrapper,\n      theme: NineCardsTheme): Tweak[TintableImageView] =\n    tivDefaultColor(theme.get(DrawerIconColor))\n}\n\nobject AccountOptions {\n\n  sealed trait AccountOption\n\n  case object CopyOption extends AccountOption\n\n  case object SyncOption extends AccountOption\n\n  case object DeleteOption extends AccountOption\n\n  case object ChangeNameOption extends AccountOption\n\n  case object PrintInfoOption extends AccountOption\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/profile/adapters/EmptyProfileAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.profile.adapters\n\nimport android.support.v7.widget.RecyclerView\nimport android.text.Html\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport android.widget.TextView\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.components.widgets.TintableImageView\nimport cards.nine.app.ui.components.widgets.tweaks.TintableImageViewTweaks._\nimport cards.nine.app.ui.profile.models.{\n  AccountsTab,\n  ProfileTab,\n  PublicationsTab,\n  SubscriptionsTab\n}\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.types.theme.{DrawerTextColor, PrimaryColor}\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\ncase class EmptyProfileAdapter(tab: ProfileTab, error: Boolean, reload: () => Unit)(\n    implicit activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[ViewHolderEmptyProfileAdapter] {\n\n  val emptyElement = 1\n\n  override def getItemCount: Int = emptyElement\n\n  override def onBindViewHolder(viewHolder: ViewHolderEmptyProfileAdapter, position: Int): Unit =\n    viewHolder.bind(tab, error).run\n\n  override def onCreateViewHolder(\n      parent: ViewGroup,\n      viewType: Int): ViewHolderEmptyProfileAdapter = {\n    val view =\n      LayoutInflater.from(parent.getContext).inflate(R.layout.empty_profile_item, parent, false)\n    ViewHolderEmptyProfileAdapter(view, reload)\n  }\n\n}\n\ncase class ViewHolderEmptyProfileAdapter(content: View, reload: () => Unit)(\n    implicit context: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    val theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView {\n\n  val textAlpha = 0.8f\n\n  lazy val root = findView(TR.profile_empty_item)\n\n  lazy val emptyProfileImage = findView(TR.profile_empty_image)\n\n  lazy val emptyProfileMessage = findView(TR.profile_empty_message)\n\n  lazy val emptyProfileButton = findView(TR.profile_empty_button)\n\n  lazy val messagePublicationsText = resGetString(R.string.emptyPublishedCollectionsMessage)\n\n  lazy val messageSubscriptionsText = resGetString(R.string.emptySubscriptionsMessage)\n\n  lazy val messageAccountsText = resGetString(R.string.emptySubscriptionsMessage)\n\n  lazy val messageAccountsErrorText = resGetString(R.string.errorConnectingGoogle)\n\n  lazy val messagePublicationsErrorText = resGetString(R.string.errorLoadingPublishedCollections)\n\n  lazy val messageSubscriptionsErrorText = resGetString(R.string.errorLoadingSubscriptions)\n\n  ((root <~ rootStyle) ~\n    (emptyProfileImage <~ imageStyle) ~\n    (emptyProfileMessage <~ textStyle) ~\n    (emptyProfileButton <~ buttonStyle)).run\n\n  def bind(tab: ProfileTab, error: Boolean)(implicit uiContext: UiContext[_]): Ui[_] = {\n\n    val messageText = tab match {\n      case PublicationsTab if error  => messagePublicationsErrorText\n      case PublicationsTab           => messagePublicationsText\n      case SubscriptionsTab if error => messageSubscriptionsErrorText\n      case SubscriptionsTab          => messageSubscriptionsText\n      case AccountsTab if error      => messageAccountsErrorText\n      case AccountsTab               => messageAccountsText\n    }\n\n    (emptyProfileImage <~ ivSrc(\n      if (error) R.drawable.placeholder_error\n      else R.drawable.placeholder_empty)) ~\n      (emptyProfileMessage <~ tvText(Html.fromHtml(messageText))) ~\n      (emptyProfileButton <~ (if (error) On.click(Ui(reload()))\n                              else vInvisible))\n\n  }\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n\n  private[this] def rootStyle(implicit context: ContextWrapper): Tweak[View] =\n    vPadding(paddingTop = resGetDimensionPixelSize(R.dimen.padding_xxxxlarge))\n\n  private[this] def imageStyle(implicit context: ContextWrapper): Tweak[TintableImageView] =\n    tivColor(theme.get(PrimaryColor))\n\n  private[this] def textStyle(implicit context: ContextWrapper): Tweak[TextView] =\n    tvColor(theme.get(DrawerTextColor).alpha(textAlpha))\n\n  private[this] def buttonStyle(implicit context: ContextWrapper): Tweak[View] =\n    vBackgroundTint(theme.get(PrimaryColor))\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/profile/adapters/SubscriptionsAdapter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.profile.adapters\n\nimport android.support.v7.widget.RecyclerView\nimport android.view.{LayoutInflater, View, ViewGroup}\nimport android.widget.TextView\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.styles.CommonStyles\nimport cards.nine.app.ui.components.widgets.tweaks.CollectionCheckBoxTweaks._\nimport cards.nine.app.ui.commons.ops.SubscriptionOps._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.models.types.theme.DrawerTextColor\nimport cards.nine.models.{NineCardsTheme, Subscription}\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid.FullDsl._\nimport macroid._\n\ncase class SubscriptionsAdapter(\n    subscriptions: Seq[Subscription],\n    onSubscribe: (String, Boolean) => Unit)(\n    implicit activityContext: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    theme: NineCardsTheme)\n    extends RecyclerView.Adapter[ViewHolderSubscriptionsAdapter] {\n\n  override def getItemCount: Int = subscriptions.size\n\n  override def onBindViewHolder(viewHolder: ViewHolderSubscriptionsAdapter, position: Int): Unit =\n    viewHolder.bind(subscriptions(position), position).run\n\n  override def onCreateViewHolder(parent: ViewGroup, i: Int): ViewHolderSubscriptionsAdapter = {\n    val view = LayoutInflater\n      .from(parent.getContext)\n      .inflate(R.layout.profile_subscription_item, parent, false)\n    ViewHolderSubscriptionsAdapter(view, onSubscribe)\n  }\n}\n\ncase class ViewHolderSubscriptionsAdapter(content: View, onSubscribe: (String, Boolean) => Unit)(\n    implicit context: ActivityContextWrapper,\n    uiContext: UiContext[_],\n    val theme: NineCardsTheme)\n    extends RecyclerView.ViewHolder(content)\n    with TypedFindView\n    with CommonStyles {\n\n  lazy val root = findView(TR.subscriptions_item_layout)\n\n  lazy val iconContent = findView(TR.subscriptions_item_content)\n\n  lazy val checkbox = findView(TR.collection_subscription_checkbox)\n\n  lazy val name = findView(TR.subscriptions_item_name)\n\n  lazy val status = findView(TR.subscriptions_item_status)\n\n  def setNameStyle(subscribed: Boolean): Tweak[TextView] = {\n    val deactivatedTitleAlpha = 0.37f\n    val alpha                 = if (subscribed) titleAlpha else deactivatedTitleAlpha\n    tvColor(theme.get(DrawerTextColor).alpha(alpha))\n  }\n\n  def setStatusStyle(subscribed: Boolean): Tweak[TextView] = {\n    val deactivatedSubtitleAlpha = 0.24f\n    val alpha                    = if (subscribed) subtitleAlpha else deactivatedSubtitleAlpha\n    tvText(\n      resGetString(if (subscribed) R.string.subscriptionActivated\n      else R.string.subscriptionDeactivated)) +\n      tvColor(theme.get(DrawerTextColor).alpha(alpha))\n  }\n\n  ((name <~ titleTextStyle) ~\n    (status <~ subtitleTextStyle)).run\n\n  def bind(subscription: Subscription, position: Int)(implicit uiContext: UiContext[_]): Ui[_] = {\n    val subscriptionColor = theme.getIndexColor(subscription.themedColorIndex)\n    val subscribed        = subscription.subscribed\n    (checkbox <~ ccbInitialize(\n      subscription.getIconSubscriptionDetail,\n      subscriptionColor,\n      theme,\n      defaultCheck = subscribed)) ~\n      (name <~ tvText(resGetString(subscription.name) getOrElse subscription.name) + setNameStyle(\n        subscribed)) ~\n      (status <~ setStatusStyle(subscribed)) ~\n      (content <~ On.click(Ui(onSubscribe(subscription.sharedCollectionId, !subscribed))))\n  }\n\n  override def findViewById(id: Int): View = content.findViewById(id)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/profile/dialog/EditAccountDeviceDialogFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.profile.dialog\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.content.DialogInterface.{OnClickListener, OnShowListener}\nimport android.os.Bundle\nimport android.support.v4.app.DialogFragment\nimport android.support.v7.app.AlertDialog\nimport android.view.LayoutInflater\nimport android.widget.LinearLayout\nimport macroid.extras.TextViewTweaks._\nimport cards.nine.app.ui.commons.CommonsExcerpt._\nimport macroid.extras.EditTextTweaks._\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.{R, TR, TypedFindView}\nimport macroid._\n\nclass EditAccountDeviceDialogFragment(\n    title: Int,\n    maybeText: Option[String],\n    action: (Option[String] => Unit))(implicit contextWrapper: ContextWrapper)\n    extends DialogFragment {\n\n  override def onCreateDialog(savedInstanceState: Bundle): Dialog = {\n\n    val dialogView = new DialogView\n\n    val dialog = new AlertDialog.Builder(getActivity)\n      .setTitle(title)\n      .setView(dialogView)\n      .setPositiveButton(android.R.string.ok, new OnClickListener {\n        override def onClick(dialog: DialogInterface, which: Int): Unit =\n          action(dialogView.readText.get)\n      })\n      .setNegativeButton(android.R.string.cancel, javaNull)\n      .create()\n\n    dialog.setOnShowListener(new OnShowListener {\n      override def onShow(dialog: DialogInterface): Unit = {\n        (dialogView.showKeyboard ~\n          (maybeText map dialogView.setText getOrElse Ui.nop)).run\n      }\n    })\n\n    dialog\n  }\n\n  class DialogView extends LinearLayout(contextWrapper.bestAvailable) with TypedFindView {\n\n    LayoutInflater.from(getActivity).inflate(R.layout.dialog_edit_text, this)\n\n    private[this] lazy val editText = findView(TR.dialog_edittext)\n\n    def setText(text: String) = editText <~ tvText(text)\n\n    def readText: Ui[Option[String]] = editText ~> text\n\n    def showKeyboard: Ui[Any] = editText <~ etShowKeyboard\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/profile/dialog/RemoveAccountDeviceDialogFragment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.profile.dialog\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.content.DialogInterface.OnClickListener\nimport android.os.Bundle\nimport android.support.v4.app.DialogFragment\nimport android.support.v7.app.AlertDialog\nimport cards.nine.commons._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ContextWrapper\n\nclass RemoveAccountDeviceDialogFragment(resourceId: String, action: (String) => Unit)(\n    implicit contextWrapper: ContextWrapper)\n    extends DialogFragment {\n\n  override def onCreateDialog(savedInstanceState: Bundle): Dialog = {\n\n    new AlertDialog.Builder(getActivity)\n      .setMessage(R.string.removeAccountSyncMessage)\n      .setPositiveButton(android.R.string.ok, new OnClickListener {\n        override def onClick(dialog: DialogInterface, which: Int): Unit =\n          action(resourceId)\n      })\n      .setNegativeButton(android.R.string.cancel, javaNull)\n      .create()\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/profile/jobs/ProfileDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.profile.jobs\n\nimport android.app.Activity\nimport cards.nine.app.ui.commons.ActivityFindViews\nimport cards.nine.app.ui.commons.adapters.sharedcollections.SharedCollectionsAdapter\nimport cards.nine.app.ui.profile.adapters.SubscriptionsAdapter\nimport cards.nine.app.ui.profile.models.ProfileTab\nimport cards.nine.models.SharedCollection\nimport com.fortysevendeg.ninecardslauncher.TR\n\nclass ProfileDOM(activity: Activity) {\n\n  import ActivityFindViews._\n\n  lazy val rootLayout = findView(TR.profile_root).run(activity)\n\n  lazy val barLayout = findView(TR.profile_appbar).run(activity)\n\n  lazy val toolbar = findView(TR.profile_toolbar).run(activity)\n\n  lazy val userContainer = findView(TR.profile_user_container).run(activity)\n\n  lazy val userAvatar = findView(TR.profile_user_avatar).run(activity)\n\n  lazy val userName = findView(TR.profile_user_name).run(activity)\n\n  lazy val userEmail = findView(TR.profile_user_email).run(activity)\n\n  lazy val tabs = findView(TR.profile_tabs).run(activity)\n\n  lazy val recyclerView = findView(TR.profile_recycler).run(activity)\n\n  lazy val loadingView = findView(TR.profile_loading).run(activity)\n\n  def getSubscriptionsAdapter: Option[SubscriptionsAdapter] =\n    recyclerView.getAdapter match {\n      case a: SubscriptionsAdapter => Some(a)\n      case _                       => None\n    }\n\n  def getSharedCollectionsAdapter: Option[SharedCollectionsAdapter] =\n    recyclerView.getAdapter match {\n      case a: SharedCollectionsAdapter => Some(a)\n      case _                           => None\n    }\n\n}\n\ntrait ProfileListener {\n\n  def onClickProfileTab(tab: ProfileTab): Unit\n\n  def onClickReloadTab(tab: ProfileTab): Unit\n\n  def onClickSynchronizeDevice(): Unit\n\n  def onClickSubscribeCollection(sharedCollectionId: String, subscribed: Boolean): Unit\n\n  def onClickCopyDevice(cloudId: String, actualName: String): Unit\n\n  def onClickRenameDevice(cloudId: String, actualName: String): Unit\n\n  def onClickDeleteDevice(cloudId: String): Unit\n\n  def onClickPrintInfoDevice(cloudId: String): Unit\n\n  def onClickOkRemoveDeviceDialog(cloudId: String): Unit\n\n  def onClickOkRenameDeviceDialog(\n      maybeName: Option[String],\n      cloudId: String,\n      actualName: String): Unit\n\n  def onClickOkOnCopyDeviceDialog(\n      maybeName: Option[String],\n      cloudId: String,\n      actualName: String): Unit\n\n  def onBarLayoutOffsetChanged(maxScroll: Float, offset: Int): Unit\n\n  def onClickAddSharedCollection(collection: SharedCollection): Unit\n\n  def onClickShareSharedCollection(collection: SharedCollection): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/profile/jobs/ProfileJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.profile.jobs\n\nimport java.util.Date\n\nimport android.app.Activity\nimport android.content.Intent\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.services.sync.SynchronizeDeviceService\nimport cards.nine.app.ui.collections.tasks.CollectionJobs\nimport cards.nine.app.ui.commons.RequestCodes._\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.commons.action_filters.{CollectionAddedActionFilter, SyncAskActionFilter}\nimport cards.nine.app.ui.profile.ProfileActivity\nimport cards.nine.app.ui.profile.models.AccountSync\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{CloudStorageDeviceSummary, RawCloudStorageDevice, SharedCollection, User}\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport com.google.android.gms.common.ConnectionResult\nimport com.google.android.gms.common.api.GoogleApiClient\nimport macroid.ActivityContextWrapper\nimport play.api.libs.json.Json\n\nclass ProfileJobs(val profileUiActions: ProfileUiActions)(\n    implicit contextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with Conversions\n    with CollectionJobs {\n\n  import ProfileActivity._\n\n  val tagDialog = \"dialog\"\n\n  var syncEnabled: Boolean = false\n\n  def driveConnected(): TaskService[Unit] = statuses.apiClient match {\n    case Some(client) if client.isConnected => loadUserAccounts(client)\n    case _ =>\n      for {\n        _ <- profileUiActions.showLoading()\n        _ <- tryToConnect()\n      } yield ()\n  }\n\n  def driveConnectionFailed(connectionResult: ConnectionResult): TaskService[Unit] =\n    if (connectionResult.hasResolution) {\n      withActivityTask { activity =>\n        connectionResult.startResolutionForResult(activity, resolveGooglePlayConnection)\n      }\n    } else {\n      profileUiActions.showEmptyAccountsContent(error = true)\n    }\n\n  def initialize(): TaskService[Unit] = {\n\n    def showUserAndConnect(user: User): TaskService[Unit] = user.email match {\n      case Some(email) =>\n        for {\n          apiClient <- di.cloudStorageProcess.createCloudStorageClient(email)\n          _         <- TaskService.right(statuses = statuses.copy(apiClient = Some(apiClient)))\n          _         <- profileUiActions.userProfile(user.userProfile.name, email, user.userProfile.avatar)\n          _         <- tryToConnect()\n        } yield ()\n      case None => TaskService.left(JobException(\"User without email\"))\n    }\n\n    for {\n      theme <- getThemeTask\n      _     <- profileUiActions.initialize(theme)\n      user  <- di.userProcess.getUser\n      _     <- showUserAndConnect(user)\n    } yield ()\n  }\n\n  def resume(): TaskService[Unit] =\n    askBroadCastTask(BroadAction(SyncAskActionFilter.action))\n\n  def stop(): TaskService[Unit] =\n    TaskService(CatchAll[JobException](statuses.apiClient foreach (_.disconnect())))\n\n  def onOffsetChanged(percentage: Float): TaskService[Unit] =\n    for {\n      _ <- profileUiActions.handleToolbarVisibility(percentage)\n      _ <- profileUiActions.handleProfileVisibility(percentage)\n    } yield ()\n\n  def accountSynced(): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.synchronizeConfiguration()\n      _ <- loadUserAccounts()\n      _ <- profileUiActions.showMessageAccountSynced()\n      _ <- TaskService.right(syncEnabled = false)\n    } yield ()\n\n  def errorSyncing(): TaskService[Unit] =\n    for {\n      _ <- profileUiActions.showSyncingError()\n      _ <- TaskService.right(syncEnabled = true)\n    } yield ()\n\n  def stateSyncing(): TaskService[Unit] =\n    TaskService.right(syncEnabled = false).map(_ => (): Unit)\n\n  def loadUserAccounts(): TaskService[Unit] =\n    withConnectedClient(loadUserAccounts(_))\n\n  def saveSharedCollection(sharedCollection: SharedCollection): TaskService[Unit] =\n    for {\n      _          <- di.trackEventProcess.addToMyCollectionsFromProfile(sharedCollection.name)\n      collection <- addSharedCollection(sharedCollection)\n      _          <- profileUiActions.showAddCollectionMessage(sharedCollection.sharedCollectionId)\n      _ <- sendBroadCastTask(\n        BroadAction(CollectionAddedActionFilter.action, Some(collection.id.toString)))\n    } yield ()\n\n  def shareCollection(sharedCollection: SharedCollection): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.shareCollectionFromProfile(sharedCollection.name)\n      _ <- di.launcherExecutorProcess.launchShare(\n        getString(R.string.shared_collection_url, sharedCollection.id))\n    } yield ()\n\n  def loadPublications(): TaskService[Unit] =\n    for {\n      _           <- di.trackEventProcess.showPublicationsContent()\n      _           <- profileUiActions.showLoading()\n      collections <- di.sharedCollectionsProcess.getPublishedCollections()\n      _ <- if (collections.isEmpty) {\n        profileUiActions.showEmptyPublicationsContent(error = false)\n      } else profileUiActions.loadPublications(collections)\n    } yield ()\n\n  def loadSubscriptions(): TaskService[Unit] =\n    for {\n      _             <- di.trackEventProcess.showSubscriptionsContent()\n      _             <- profileUiActions.showLoading()\n      subscriptions <- di.sharedCollectionsProcess.getSubscriptions()\n      _ <- if (subscriptions.isEmpty) {\n        profileUiActions.showEmptySubscriptionsContent(error = false)\n      } else profileUiActions.setSubscriptionsAdapter(subscriptions)\n    } yield ()\n\n  def changeSubscriptionStatus(\n      sharedCollectionId: String,\n      subscribeStatus: Boolean): TaskService[Unit] = {\n\n    val subscribeService =\n      for {\n        _ <- di.trackEventProcess.subscribeToCollection(sharedCollectionId)\n        _ <- di.sharedCollectionsProcess.subscribe(sharedCollectionId)\n      } yield ()\n\n    val unsubscribeService =\n      for {\n        _ <- di.trackEventProcess.unsubscribeFromCollection(sharedCollectionId)\n        _ <- di.sharedCollectionsProcess.unsubscribe(sharedCollectionId)\n      } yield ()\n\n    for {\n      _ <- if (subscribeStatus) subscribeService else unsubscribeService\n      _ <- profileUiActions.showUpdatedSubscriptions(sharedCollectionId, subscribeStatus)\n    } yield ()\n  }\n\n  def activityResult(requestCode: Int, resultCode: Int, data: Intent): TaskService[Unit] =\n    (requestCode, resultCode) match {\n      case (`resolveGooglePlayConnection`, Activity.RESULT_OK) =>\n        tryToConnect()\n      case (`resolveGooglePlayConnection`, _) =>\n        profileUiActions.showEmptyAccountsContent(error = true)\n    }\n\n  def quit(): TaskService[Unit] =\n    for {\n      _ <- di.trackEventProcess.logout()\n      _ <- di.collectionProcess.cleanCollections()\n      _ <- di.deviceProcess.deleteAllDockApps()\n      _ <- di.momentProcess.deleteAllMoments()\n      _ <- di.widgetsProcess.deleteAllWidgets()\n      _ <- di.userProcess.unregister\n      _ <- withActivityTask { activity =>\n        activity.setResult(ResultCodes.logoutSuccessful)\n        activity.finish()\n      }\n    } yield ()\n\n  def launchService(): TaskService[Unit] =\n    if (syncEnabled) {\n      for {\n        _ <- profileUiActions.showMessageSyncingAccount\n        _ <- withActivityTask { activity =>\n          syncEnabled = false\n          activity.startService(new Intent(activity, classOf[SynchronizeDeviceService]))\n        }\n      } yield ()\n    } else TaskService.empty\n\n  def deleteDevice(cloudId: String): TaskService[Unit] =\n    withConnectedClient { client =>\n      for {\n        _ <- di.trackEventProcess.deleteConfiguration()\n        _ <- profileUiActions.showLoading()\n        _ <- di.cloudStorageProcess.deleteCloudStorageDevice(client, cloudId)\n        _ <- loadUserAccounts(client, Seq(cloudId))\n      } yield ()\n    }\n\n  def copyDevice(\n      maybeName: Option[String],\n      cloudId: String,\n      actualName: String): TaskService[Unit] =\n    copyOrRenameDevice(maybeName, cloudId, actualName, copy = true)\n\n  def renameDevice(\n      maybeName: Option[String],\n      cloudId: String,\n      actualName: String): TaskService[Unit] =\n    copyOrRenameDevice(maybeName, cloudId, actualName, copy = false)\n\n  def printDeviceInfo(cloudId: String): TaskService[Unit] = {\n\n    def printDeviceInfo(device: RawCloudStorageDevice, prettyJson: String): Unit = {\n      AppLog.info(s\"----------------------------- Device Info -----------------------------\")\n      AppLog.info(s\" Cloud id: ${device.cloudId}\")\n      AppLog.info(s\" UUID: ${device.uuid}\")\n      AppLog.info(s\" Device name: ${device.title}\")\n      AppLog.info(s\" Device id: ${device.deviceId}\")\n      AppLog.info(s\" Created date: ${device.createdDate}\")\n      AppLog.info(s\" Modified date: ${device.modifiedDate}\")\n      AppLog.info(s\" JSON\")\n      AppLog.info(prettyJson)\n      AppLog.info(s\"----------------------------- Device Info -----------------------------\")\n    }\n\n    withConnectedClient { client =>\n      for {\n        device <- di.cloudStorageProcess.getRawCloudStorageDevice(client, cloudId)\n        prettyJson <- TaskService(\n          CatchAll[JobException](Json.prettyPrint(Json.parse(device.json))))\n      } yield printDeviceInfo(device, prettyJson)\n    }\n\n  }\n\n  private[this] def copyOrRenameDevice(\n      maybeName: Option[String],\n      cloudId: String,\n      actualName: String,\n      copy: Boolean): TaskService[Unit] = {\n\n    def createOrUpdate(name: String, client: GoogleApiClient, cloudId: String) =\n      for {\n        device <- di.cloudStorageProcess.getCloudStorageDevice(client, cloudId)\n        maybeCloudId = if (copy) None else Some(cloudId)\n        _ <- if (copy) di.trackEventProcess.copyConfiguration()\n        else di.trackEventProcess.changeConfigurationName()\n        _ <- di.cloudStorageProcess.createOrUpdateCloudStorageDevice(\n          client,\n          maybeCloudId,\n          device.data.copy(deviceName = name))\n      } yield ()\n\n    maybeName match {\n      case Some(name) if name.nonEmpty =>\n        withConnectedClient { client =>\n          for {\n            _ <- profileUiActions.showLoading()\n            _ <- createOrUpdate(name, client, cloudId)\n            _ <- loadUserAccounts(client)\n          } yield ()\n        }\n      case _ => profileUiActions.showInvalidConfigurationNameError()\n    }\n  }\n\n  private[this] def tryToConnect(): TaskService[Unit] =\n    TaskService(CatchAll[JobException](statuses.apiClient foreach (_.connect())))\n\n  private[this] def loadUserAccounts(\n      client: GoogleApiClient,\n      filterOutResourceIds: Seq[String] = Seq.empty): TaskService[Unit] = {\n\n    def toAccountSync(d: CloudStorageDeviceSummary, current: Boolean = false): AccountSync =\n      AccountSync.syncDevice(\n        title = d.deviceName,\n        syncDate = d.modifiedDate,\n        current = current,\n        cloudId = d.cloudId)\n\n    def order(seq: Seq[CloudStorageDeviceSummary]): Seq[CloudStorageDeviceSummary] =\n      seq.sortBy(_.modifiedDate)(Ordering[Date].reverse)\n\n    def createSync(devices: Seq[CloudStorageDeviceSummary]) =\n      devices.partition(_.currentDevice) match {\n        case (current, other) =>\n          val currentDevices = order(current)\n          val currentDevicesWithHeader = currentDevices.headOption map { device =>\n            Seq(\n              AccountSync.header(getString(R.string.syncCurrent)),\n              toAccountSync(device, current = true))\n          } getOrElse Seq.empty\n          val otherDevices = order(other ++ currentDevices.drop(1)) match {\n            case seq if seq.isEmpty => Seq.empty\n            case seq =>\n              AccountSync.header(getString(R.string.syncHeaderDevices)) +:\n                (seq map (toAccountSync(_)))\n          }\n          currentDevicesWithHeader ++ otherDevices\n      }\n\n    def loadAccounts(client: GoogleApiClient, filterOutResourceIds: Seq[String] = Seq.empty) = {\n      di.cloudStorageProcess.getCloudStorageDevices(client).map { devices =>\n        createSync(devices.filterNot(d => filterOutResourceIds.contains(d.cloudId)))\n      }\n    }\n\n    for {\n      _            <- di.trackEventProcess.showAccountsContent()\n      _            <- profileUiActions.showLoading()\n      accountsSync <- loadAccounts(client, filterOutResourceIds)\n      _            <- TaskService.right(syncEnabled = true)\n      _ <- if (accountsSync.isEmpty) {\n        profileUiActions.showEmptyAccountsContent(error = false)\n      } else {\n        profileUiActions.setAccountsAdapter(accountsSync)\n      }\n    } yield ()\n  }\n\n  private[this] def withConnectedClient(\n      f: (GoogleApiClient) => TaskService[Unit]): TaskService[Unit] = {\n\n    def loadUserEmail() = di.userProcess.getUser.resolveRight(\n      mapRight = _.email match {\n        case Some(email) => Right(email)\n        case None        => Left(JobException(\"User without email\"))\n      }\n    )\n\n    def loadUserInfo(): TaskService[Unit] =\n      for {\n        _         <- profileUiActions.showLoading()\n        email     <- loadUserEmail()\n        apiClient <- di.cloudStorageProcess.createCloudStorageClient(email)\n        _         <- TaskService.right(statuses = statuses.copy(apiClient = Some(apiClient)))\n        _         <- tryToConnect()\n      } yield ()\n\n    statuses.apiClient match {\n      case Some(client) if client.isConnected =>\n        f(client)\n      case Some(client) =>\n        for {\n          _ <- profileUiActions.showLoading()\n          _ <- tryToConnect()\n        } yield ()\n      case _ =>\n        loadUserInfo()\n    }\n  }\n\n  protected def getString(res: Int): String = resGetString(res)\n\n  protected def getString(res: Int, args: AnyRef*): String =\n    resGetString(res, args)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/profile/jobs/ProfileUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.profile.jobs\n\nimport android.support.design.widget.TabLayout.Tab\nimport android.support.design.widget.{AppBarLayout, TabLayout}\nimport android.support.v4.app.{DialogFragment, Fragment, FragmentManager}\nimport android.support.v7.app.AppCompatActivity\nimport android.support.v7.widget.LinearLayoutManager\nimport android.view.View\nimport android.widget.ImageView\nimport cards.nine.app.ui.commons.AsyncImageTweaks._\nimport cards.nine.app.ui.commons.CommonsTweak._\nimport macroid.extras.UIActionsExtras._\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.commons.adapters.sharedcollections.SharedCollectionsAdapter\nimport cards.nine.app.ui.commons.dialogs.wizard.{\n  CollectionsWizardInline,\n  ProfileWizardInline,\n  WizardInlinePreferences\n}\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.drawables.{CharDrawable, PathMorphDrawable}\nimport cards.nine.app.ui.profile.adapters.AccountOptions._\nimport cards.nine.app.ui.profile.adapters.{\n  AccountsAdapter,\n  EmptyProfileAdapter,\n  SubscriptionsAdapter\n}\nimport cards.nine.app.ui.profile.dialog.{\n  EditAccountDeviceDialogFragment,\n  RemoveAccountDeviceDialogFragment\n}\nimport cards.nine.app.ui.profile.models._\nimport cards.nine.commons._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.PublishedByOther\nimport cards.nine.models.types.theme.{CardLayoutBackgroundColor, PrimaryColor}\nimport cards.nine.models.{NineCardsTheme, SharedCollection, Subscription}\nimport macroid.extras.DeviceVersion.Lollipop\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.RecyclerViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.ProgressBarTweaks._\nimport macroid.extras.TabLayoutTweaks._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\n\nclass ProfileUiActions(dom: ProfileDOM, listener: ProfileListener)(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_])\n    extends TabLayout.OnTabSelectedListener\n    with AppBarLayout.OnOffsetChangedListener\n    with ImplicitsUiExceptions {\n\n  val tagDialog = \"dialog\"\n\n  implicit lazy val systemBarsTint = new SystemBarsTint\n\n  lazy val wizardInlinePreferences = new WizardInlinePreferences()\n\n  lazy val iconIndicatorDrawable = PathMorphDrawable(\n    defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_default),\n    padding = resGetDimensionPixelSize(R.dimen.padding_icon_home_indicator))\n\n  implicit var theme = AppUtils.getDefaultTheme\n\n  def initialize(nineCardsTheme: NineCardsTheme): TaskService[Unit] = {\n\n    def initActionBar = Ui {\n      activityContextWrapper.original.get match {\n        case Some(activity: AppCompatActivity) =>\n          activity.setSupportActionBar(dom.toolbar)\n          activity.getSupportActionBar.setDisplayHomeAsUpEnabled(true)\n          activity.getSupportActionBar.setHomeAsUpIndicator(iconIndicatorDrawable)\n        case _ =>\n      }\n    }\n\n    (Ui(theme = nineCardsTheme) ~\n      (dom.rootLayout <~ vBackgroundColor(theme.get(CardLayoutBackgroundColor))) ~\n      (dom.userContainer <~ vBackgroundColor(theme.get(PrimaryColor))) ~\n      (dom.barLayout <~ vBackgroundColor(theme.get(PrimaryColor))) ~\n      (dom.loadingView <~ pbColor(theme.get(PrimaryColor))) ~\n      (dom.tabs <~ tlAddTabs(\n        (resGetString(R.string.accounts), AccountsTab),\n        (resGetString(R.string.publications), PublicationsTab),\n        (resGetString(R.string.subscriptions), SubscriptionsTab))) ~\n      (dom.tabs <~ tlAddListener(this)) ~\n      (dom.recyclerView <~\n        rvLayoutManager(new LinearLayoutManager(activityContextWrapper.application))) ~\n      systemBarsTint.updateStatusColor(theme.get(PrimaryColor)) ~\n      initActionBar ~\n      Ui(dom.barLayout.addOnOffsetChangedListener(this))).toService()\n  }\n\n  def showLoading(): TaskService[Unit] =\n    ((dom.loadingView <~ vVisible) ~ (dom.recyclerView <~ vInvisible)).toService()\n\n  def showAddCollectionMessage(sharedCollectionId: String): TaskService[Unit] = {\n\n    def prepareAdapter: TaskService[SharedCollectionsAdapter] =\n      dom.getSharedCollectionsAdapter match {\n        case Some(adapter) =>\n          val newCollections = adapter.sharedCollections map {\n            case col if col.sharedCollectionId == sharedCollectionId =>\n              col.copy(locallyAdded = Some(true))\n            case col => col\n          }\n          TaskService.right(adapter.copy(sharedCollections = newCollections))\n        case _ =>\n          TaskService.left(UiException(\"Can't find SharedCollectionsAdapter\"))\n      }\n\n    for {\n      _       <- showMessage(R.string.collectionAdded).toService()\n      adapter <- prepareAdapter\n      _       <- (dom.recyclerView <~ rvSwapAdapter(adapter)).toService()\n    } yield ()\n  }\n\n  // TODO Remove when we've got different states for the switch - issue #783\n  def refreshCurrentSubscriptions(): TaskService[Unit] =\n    (dom.recyclerView <~ rvSwapAdapter(dom.recyclerView.getAdapter)).toService()\n\n  def showUpdatedSubscriptions(\n      sharedCollectionId: String,\n      subscribed: Boolean): TaskService[Unit] = {\n\n    def prepareAdapter: TaskService[SubscriptionsAdapter] =\n      dom.getSubscriptionsAdapter match {\n        case Some(adapter) =>\n          val subscriptions = adapter.subscriptions map {\n            case subscription if subscription.sharedCollectionId == sharedCollectionId =>\n              subscription.copy(subscribed = subscribed)\n            case subscription => subscription\n          }\n          TaskService.right(adapter.copy(subscriptions = subscriptions))\n        case _ =>\n          TaskService.left(UiException(\"Can't find SubscriptionsAdapter\"))\n      }\n\n    for {\n      adapter <- prepareAdapter\n      _       <- (dom.recyclerView <~ rvSwapAdapter(adapter)).toService()\n    } yield ()\n\n  }\n\n  def showErrorSubscribing(triedToSubscribe: Boolean): TaskService[Unit] =\n    showMessage(\n      if (triedToSubscribe) R.string.errorSubscribing\n      else R.string.errorUnsubscribing).toService()\n\n  def showContactUsError(): TaskService[Unit] =\n    uiShortToast(R.string.contactUsError).toService()\n\n  def showSyncingError(): TaskService[Unit] =\n    (showMessage(R.string.errorSyncing) ~ (dom.loadingView <~ vInvisible)).toService()\n\n  def showInvalidConfigurationNameError(): TaskService[Unit] =\n    (dom.rootLayout <~ vSnackbarShort(res = R.string.errorEmptyNameForDevice)).toService()\n\n  def showErrorSavingCollectionInScreen(): TaskService[Unit] =\n    showError(\n      R.string.errorSavingPublicCollections,\n      () => listener.onClickProfileTab(PublicationsTab)).toService()\n\n  def showMessageSyncingAccount(): TaskService[Unit] =\n    (showMessage(R.string.syncingAccount)).toService()\n\n  def showMessageAccountSynced(): TaskService[Unit] =\n    (showMessage(R.string.accountSynced) ~ (dom.loadingView <~ vInvisible)).toService()\n\n  def userProfile(\n      maybeName: Option[String],\n      email: String,\n      avatarUrl: Option[String]): TaskService[Unit] = {\n    val name = maybeName.getOrElse(email)\n    ((dom.userName <~ tvText(name)) ~\n      (dom.userEmail <~ tvText(email)) ~\n      (dom.userAvatar <~\n        (avatarUrl map ivUri getOrElse {\n          val drawable = CharDrawable(name.substring(0, 1).toUpperCase)\n          ivSrc(drawable)\n        }) <~\n        menuAvatarStyle)).toService()\n  }\n\n  def setAccountsAdapter(items: Seq[AccountSync]): TaskService[Unit] =\n    ((dom.recyclerView <~ vVisible <~ rvAdapter(AccountsAdapter(items, accountClickListener))) ~\n      (dom.loadingView <~ vInvisible)).toService()\n\n  def setSubscriptionsAdapter(items: Seq[Subscription]): TaskService[Unit] =\n    ((dom.recyclerView <~ vVisible <~ rvAdapter(\n      SubscriptionsAdapter(items, listener.onClickSubscribeCollection))) ~\n      (dom.loadingView <~ vInvisible)).toService()\n\n  def handleToolbarVisibility(percentage: Float): TaskService[Unit] =\n    (dom.toolbar match {\n      case t if percentage >= 0.5 && t.getVisibility == View.VISIBLE =>\n        dom.toolbar <~ SnailsCommons.applyFadeOut()\n      case t if percentage < 0.5 && t.getVisibility == View.INVISIBLE =>\n        dom.toolbar <~ SnailsCommons.applyFadeIn()\n      case _ => Ui.nop\n    }).toService()\n\n  def handleProfileVisibility(percentage: Float): TaskService[Unit] = {\n    val alpha = if (percentage <= 0.5f) 1f - (percentage * 2) else 0f\n    (dom.userContainer <~ vAlpha(alpha)).toService()\n  }\n\n  def showDialogForDeleteDevice(cloudId: String): TaskService[Unit] =\n    showDialog(\n      new RemoveAccountDeviceDialogFragment(cloudId, listener.onClickOkRemoveDeviceDialog))\n\n  def showDialogForCopyDevice(cloudId: String, actualName: String): TaskService[Unit] =\n    showDialog(\n      new EditAccountDeviceDialogFragment(\n        title = R.string.copyAccountSyncDialogTitle,\n        maybeText = None,\n        action = listener.onClickOkOnCopyDeviceDialog(_, cloudId, actualName)))\n\n  def showDialogForRenameDevice(cloudId: String, actualName: String): TaskService[Unit] =\n    showDialog(\n      new EditAccountDeviceDialogFragment(\n        title = R.string.renameAccountSyncDialogTitle,\n        maybeText = Some(actualName),\n        action = listener.onClickOkRenameDeviceDialog(_, cloudId, actualName)))\n\n  def loadPublications(sharedCollections: Seq[SharedCollection]): TaskService[Unit] = {\n    val adapter = SharedCollectionsAdapter(\n      sharedCollections,\n      listener.onClickAddSharedCollection,\n      listener.onClickShareSharedCollection)\n    ((dom.recyclerView <~\n      vVisible <~\n      rvLayoutManager(adapter.getLayoutManager) <~\n      rvAdapter(adapter)) ~\n      (dom.loadingView <~ vInvisible)).toService()\n  }\n\n  def showEmptyPublicationsContent(error: Boolean): TaskService[Unit] =\n    showEmptyContent(PublicationsTab, error, () => listener.onClickReloadTab(PublicationsTab))\n      .toService()\n\n  def showEmptySubscriptionsContent(error: Boolean): TaskService[Unit] =\n    showEmptyContent(SubscriptionsTab, error, () => listener.onClickReloadTab(SubscriptionsTab))\n      .toService()\n\n  def showEmptyAccountsContent(error: Boolean): TaskService[Unit] =\n    showEmptyContent(AccountsTab, error, () => listener.onClickReloadTab(AccountsTab)).toService()\n\n  def openProfileWizardInline(): TaskService[Unit] =\n    (if (wizardInlinePreferences.shouldBeShowed(ProfileWizardInline)) {\n       dom.rootLayout <~ vLauncherWizardSnackbar(\n         ProfileWizardInline,\n         forceNavigationBarHeight = false)\n     } else {\n       Ui.nop\n     }).toService()\n\n  override def onTabReselected(tab: Tab): Unit = {}\n\n  override def onTabUnselected(tab: Tab): Unit = {}\n\n  override def onTabSelected(tab: Tab): Unit = tab.getTag match {\n    case tab: ProfileTab => listener.onClickProfileTab(tab)\n    case _               =>\n  }\n\n  override def onOffsetChanged(appBarLayout: AppBarLayout, offset: Int): Unit = {\n    val maxScroll = appBarLayout.getTotalScrollRange.toFloat\n    listener.onBarLayoutOffsetChanged(maxScroll, offset)\n  }\n\n  private[this] def showEmptyContent(\n      tab: ProfileTab,\n      error: Boolean,\n      reload: () => Unit): Ui[Any] =\n    (dom.recyclerView <~\n      vVisible <~\n      rvAdapter(EmptyProfileAdapter(tab, error, reload))) ~\n      (dom.loadingView <~ vInvisible)\n\n  private[this] def showDialog(dialog: DialogFragment): TaskService[Unit] =\n    TaskService {\n      CatchAll[UiException] {\n        activityContextWrapper.original.get match {\n          case Some(activity: AppCompatActivity) =>\n            val ft = activity.getSupportFragmentManager.beginTransaction()\n            Option(activity.getSupportFragmentManager.findFragmentByTag(tagDialog)) foreach ft.remove\n            ft.addToBackStack(javaNull)\n            dialog.show(ft, tagDialog)\n          case _ =>\n        }\n      }\n    }\n\n  private[this] def showMessage(res: Int): Ui[Any] =\n    dom.rootLayout <~ vSnackbarShort(res)\n\n  private[this] def showError(message: Int, clickAction: () => Unit): Ui[Any] =\n    (dom.rootLayout <~ vSnackbarIndefiniteAction(message, R.string.buttonErrorReload, clickAction)) ~\n      (dom.loadingView <~ vInvisible)\n\n  private[this] def accountClickListener(\n      accountOption: AccountOption,\n      accountSync: AccountSync): Unit =\n    (accountOption, accountSync.cloudId) match {\n      case (SyncOption, _) => listener.onClickSynchronizeDevice()\n      case (DeleteOption, Some(cloudId)) =>\n        listener.onClickDeleteDevice(cloudId)\n      case (CopyOption, Some(cloudId)) =>\n        listener.onClickCopyDevice(cloudId, accountSync.title)\n      case (ChangeNameOption, Some(cloudId)) =>\n        listener.onClickRenameDevice(cloudId, accountSync.title)\n      case (PrintInfoOption, Some(cloudId)) =>\n        listener.onClickPrintInfoDevice(cloudId)\n      case _ =>\n    }\n\n  // Styles\n\n  private[this] def menuAvatarStyle(implicit context: ContextWrapper): Tweak[ImageView] =\n    Lollipop ifSupportedThen {\n      vCircleOutlineProvider()\n    } getOrElse Tweak.blank\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/profile/models/Model.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.profile.models\n\nimport java.util.Date\n\nimport cards.nine.commons.contexts.ContextSupport\nimport com.fortysevendeg.ninecardslauncher.R\nimport org.ocpsoft.prettytime.PrettyTime\n\nsealed trait AccountSyncType\n\ncase object Header extends AccountSyncType\n\ncase class Device(current: Boolean) extends AccountSyncType\n\ncase class AccountSync(\n    title: String,\n    accountSyncType: AccountSyncType,\n    cloudId: Option[String] = None,\n    subtitle: Option[String] = None)\n\nobject AccountSync {\n\n  def header(title: String): AccountSync =\n    AccountSync(title = title, accountSyncType = Header)\n\n  def syncDevice(title: String, syncDate: Date, current: Boolean = false, cloudId: String)(\n      implicit context: ContextSupport): AccountSync = {\n    val time = new PrettyTime().format(syncDate)\n    AccountSync(\n      title = title,\n      accountSyncType = Device(current),\n      cloudId = Option(cloudId),\n      subtitle = Option(context.getResources.getString(R.string.syncLastSynced, time)))\n  }\n\n}\n\nsealed trait ProfileTab\n\ncase object PublicationsTab extends ProfileTab\n\ncase object SubscriptionsTab extends ProfileTab\n\ncase object AccountsTab extends ProfileTab\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/share/SharedContentActivity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.share\n\nimport android.app.Activity\nimport android.os.Bundle\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.support.v7.app.AppCompatActivity\nimport cards.nine.app.commons.ContextSupportProvider\nimport cards.nine.app.ui.commons.{ActivityUiContext, AppUtils, UiContext}\nimport cards.nine.app.ui.share.SharedContentActivity._\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.app.ui.share.models.SharedContent\nimport cards.nine.models.NineCardsTheme\nimport com.fortysevendeg.ninecardslauncher.TypedFindView\nimport macroid.{ActivityContextWrapper, Contexts, FragmentManagerContext}\n\nclass SharedContentActivity\n    extends AppCompatActivity\n    with Contexts[AppCompatActivity]\n    with ContextSupportProvider\n    with TypedFindView { self =>\n\n  implicit lazy val uiContext: UiContext[Activity] = ActivityUiContext(self)\n\n  lazy val sharedContentJobs: SharedContentJobs = createSharedContentJob\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n    (for {\n      _ <- sharedContentJobs.initialize()\n      _ <- sharedContentJobs.receivedIntent(getIntent)\n    } yield ()).resolveAsync()\n  }\n}\n\nobject SharedContentActivity {\n\n  var statuses = SharedContentStatuses()\n\n  def createSharedContentJob(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]): SharedContentJobs =\n    new SharedContentJobs(new SharedContentUiActions())\n\n}\n\ncase class SharedContentStatuses(\n    theme: NineCardsTheme = AppUtils.getDefaultTheme,\n    sharedContent: Option[SharedContent] = None)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/share/SharedContentJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.share\n\nimport java.net.URL\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.provider.MediaStore\nimport android.webkit.URLUtil\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.app.ui.share.SharedContentActivity._\nimport cards.nine.app.ui.share.models.{SharedContent, Web}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.{TaskService, _}\nimport cards.nine.models.types.ShortcutCardType\nimport cards.nine.models.{CardData, IconResize, NineCardsIntent, NineCardsIntentExtras}\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ActivityContextWrapper\n\nimport scala.util.Try\n\nclass SharedContentJobs(val sharedContentUiActions: SharedContentUiActions)(\n    implicit activityContextWrapper: ActivityContextWrapper)\n    extends Jobs {\n\n  def initialize(): TaskService[Unit] =\n    for {\n      theme <- getThemeTask\n      _     <- TaskService.right(statuses = statuses.copy(theme = theme))\n    } yield ()\n\n  def receivedIntent(intent: Intent): TaskService[Unit] = {\n    def readImageUri(intent: Intent): Option[Uri] = {\n      Option(intent.getClipData) flatMap { clipData =>\n        if (clipData.getItemCount > 0)\n          Option(clipData.getItemAt(0)) flatMap (d => Option(d.getUri))\n        else None\n      }\n    }\n\n    def isLink(link: String): Boolean = URLUtil.isValidUrl(link)\n\n    def showErrorContentNotSupported() =\n      for {\n        _ <- di.trackEventProcess.sharedContentReceived(false)\n        _ <- sharedContentUiActions.showErrorContentNotSupported()\n      } yield ()\n\n    def extractFirstURL(text: String): Option[String] =\n      text.split(\"\"\"\\s+\"\"\") find { str =>\n        (str.startsWith(\"http://\") || str.startsWith(\"https://\")) && isLink(str)\n      }\n\n    val contentTypeText = \"text/plain\"\n\n    Option(intent) map { i =>\n      val action     = Option(i.getAction)\n      val intentType = Option(i.getType)\n      val extraText  = readStringValue(i, Intent.EXTRA_TEXT)\n      val urlInText  = extraText flatMap extractFirstURL\n      val subject = readStringValue(i, Intent.EXTRA_SUBJECT) match {\n        case Some(s) => Option(s)\n        case _       => extraText\n      }\n\n      (action, intentType, urlInText) match {\n        case (Some(Intent.ACTION_SEND), Some(`contentTypeText`), Some(url)) =>\n          val sharedContent =\n            SharedContent(\n              contentType = Web,\n              title = subject getOrElse resGetString(R.string.sharedContentDefaultTitle),\n              content = url,\n              image = readImageUri(i))\n          statuses = statuses.copy(sharedContent = Some(sharedContent))\n          for {\n            _           <- di.trackEventProcess.sharedContentReceived(true)\n            collections <- di.collectionProcess.getCollections\n            _           <- sharedContentUiActions.showChooseCollection(collections)\n          } yield ()\n        case (Some(Intent.ACTION_SEND), Some(`contentTypeText`), None) =>\n          sharedContentUiActions.showErrorEmptyContent()\n        case (Some(Intent.ACTION_SEND), _, _) => showErrorContentNotSupported()\n        case _                                => sharedContentUiActions.showUnexpectedError()\n      }\n    } getOrElse TaskService.empty\n\n  }\n\n  def collectionChosen(collectionId: Int): TaskService[Unit] = {\n\n    def createRequest(sharedContent: SharedContent, imagePath: String): CardData = {\n\n      val intent =\n        new Intent(Intent.ACTION_VIEW, Uri.parse(sharedContent.content))\n\n      val nineCardIntent = NineCardsIntent(NineCardsIntentExtras())\n      nineCardIntent.fill(intent)\n\n      CardData(\n        term = sharedContent.title,\n        packageName = None,\n        cardType = ShortcutCardType,\n        intent = nineCardIntent,\n        imagePath = Option(imagePath))\n    }\n\n    def saveBitmap(maybeUri: Option[Uri]): TaskService[String] = {\n      maybeUri match {\n        case Some(uri) =>\n          val iconSize = resGetDimensionPixelSize(R.dimen.size_icon_app_medium)\n          di.deviceProcess.saveShortcutIcon(\n            MediaStore.Images.Media\n              .getBitmap(activityContextWrapper.bestAvailable.getContentResolver, uri),\n            Some(IconResize(iconSize, iconSize)))\n        case _ => TaskService.right(\"\")\n      }\n    }\n\n    def addCard(sharedContent: SharedContent): TaskService[Unit] =\n      for {\n        imagePath <- saveBitmap(sharedContent.image)\n        _ <- di.collectionProcess\n          .addCards(collectionId, Seq(createRequest(sharedContent, imagePath)))\n      } yield ()\n\n    statuses.sharedContent match {\n      case Some(sharedContent) =>\n        for {\n          _ <- addCard(sharedContent)\n          _ <- sharedContentUiActions.showSuccess()\n        } yield ()\n      case _ => sharedContentUiActions.showUnexpectedError()\n    }\n  }\n\n  def dialogDismissed(): TaskService[Unit] = sharedContentUiActions.close()\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/share/SharedContentUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.share\n\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport macroid.extras.UIActionsExtras._\nimport cards.nine.app.ui.commons.UiContext\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.components.dialogs.CollectionDialog\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.Collection\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.{ActivityContextWrapper, FragmentManagerContext, Ui}\nimport SharedContentActivity._\nimport cards.nine.models.NineCardsTheme\n\nclass SharedContentUiActions(\n    implicit activityContextWrapper: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_]) {\n\n  val tagDialog = \"dialog\"\n\n  lazy val sharedContentJob: SharedContentJobs = createSharedContentJob\n\n  implicit def theme: NineCardsTheme = statuses.theme\n\n  def showChooseCollection(collections: Seq[Collection]): TaskService[Unit] =\n    Ui {\n      new CollectionDialog(moments = collections, onCollection = (collectionId) => {\n        sharedContentJob\n          .collectionChosen(collectionId)\n          .resolveAsyncServiceOr(_ => showUnexpectedError())\n      }, onDismissDialog = () => {\n        sharedContentJob.dialogDismissed().resolveAsync()\n      }).show(fragmentManagerContext.manager, tagDialog)\n    }.toService()\n\n  def showSuccess(): TaskService[Unit] =\n    (uiShortToast(R.string.sharedCardAdded) ~ finishUi()).toService()\n\n  def showErrorEmptyContent(): TaskService[Unit] =\n    (uiShortToast(R.string.sharedContentErrorEmpty) ~ finishUi()).toService()\n\n  def showErrorContentNotSupported(): TaskService[Unit] =\n    (uiLongToast(R.string.sharedContentErrorNotSupported) ~ finishUi()).toService()\n\n  def showUnexpectedError(): TaskService[Unit] =\n    (uiShortToast(R.string.sharedContentErrorUnexpected) ~ finishUi()).toService()\n\n  def close(): TaskService[Unit] = finishUi().toService()\n\n  def finishUi(): Ui[Any] = Ui(activityContextWrapper.getOriginal.finish())\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/share/models/Models.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.share.models\n\nimport android.net.Uri\n\nsealed trait ContentType\n\ncase object Web extends ContentType\n\ncase class SharedContent(\n    contentType: ContentType,\n    title: String,\n    content: String,\n    image: Option[Uri])\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/wizard/WizardActivity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.wizard\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport android.support.v4.app.{Fragment, FragmentManager}\nimport android.support.v7.app.AppCompatActivity\nimport cards.nine.app.commons.ContextSupportProvider\nimport cards.nine.app.ui.commons.ops.TaskServiceOps._\nimport cards.nine.app.ui.commons.{ActivityUiContext, SynchronizeDeviceJobs, UiContext}\nimport cards.nine.app.ui.wizard.jobs._\nimport cards.nine.app.ui.wizard.jobs.uiactions._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.PackagesByCategory\nimport cards.nine.models.types.NineCardsMoment\nimport cards.nine.process.cloud.CloudStorageClientListener\nimport cards.nine.process.social.{SocialProfileClientListener, SocialProfileProcessException}\nimport cards.nine.process.user.UserException\nimport cards.nine.process.userv1.UserV1Exception\nimport cats.implicits._\nimport com.fortysevendeg.ninecardslauncher.{R, TypedFindView}\nimport com.google.android.gms.common.ConnectionResult\nimport macroid.{ActivityContextWrapper, Contexts, FragmentManagerContext}\n\nimport WizardActivity._\n\nclass WizardActivity\n    extends AppCompatActivity\n    with Contexts[AppCompatActivity]\n    with ContextSupportProvider\n    with TypedFindView\n    with SocialProfileClientListener\n    with CloudStorageClientListener\n    with WizardUiListener { self =>\n\n  implicit lazy val uiContext: UiContext[Activity] = ActivityUiContext(self)\n\n  lazy val wizardJobs = createWizardJobs\n\n  lazy val newConfigurationJobs = createNewConfigurationJobs\n\n  lazy val loadConfigurationJobs = new LoadConfigurationJobs\n\n  lazy val synchronizeDeviceJobs = new SynchronizeDeviceJobs\n\n  override def onCreate(savedInstanceState: Bundle): Unit = {\n    super.onCreate(savedInstanceState)\n    setContentView(R.layout.wizard_activity)\n    wizardJobs.initialize().resolveAsync()\n  }\n\n  override def onStop(): Unit = {\n    wizardJobs.stop().resolveAsync()\n    super.onStop()\n  }\n\n  override def onBackPressed(): Unit = {}\n\n  override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit =\n    wizardJobs.activityResult(requestCode, resultCode, data).resolveAsyncServiceOr(onException)\n\n  override def onRequestPermissionsResult(\n      requestCode: Int,\n      permissions: Array[String],\n      grantResults: Array[Int]): Unit =\n    wizardJobs\n      .requestPermissionsResult(requestCode, permissions, grantResults)\n      .resolveAsyncServiceOr(onException)\n\n  override def onClickAcceptTermsButton(): Unit =\n    wizardJobs.connectAccount().resolveAsync()\n\n  override def onClickVisitTermsButton(): Unit =\n    wizardJobs.showTermOfUseWebsite().resolveAsync()\n\n  override def onClickSelectV1DeviceButton(packages: Seq[PackagesByCategory]): Unit =\n    wizardJobs\n      .deviceSelected(packages)\n      .resolveAsyncServiceOr(_ => wizardJobs.visibilityUiActions.goToUser())\n\n  override def onClickSelectDeviceButton(maybeCloudId: Option[String]): Unit =\n    wizardJobs\n      .deviceSelected(maybeCloudId)\n      .resolveAsyncServiceOr(_ => wizardJobs.visibilityUiActions.goToUser())\n\n  override def onClickFinishWizardButton(): Unit =\n    wizardJobs.finishWizard().resolveAsync()\n\n  override def onPlusConnectionSuspended(cause: Int): Unit = {}\n\n  override def onPlusConnected(): Unit =\n    wizardJobs.plusConnected().resolveAsyncServiceOr(onException)\n\n  override def onPlusConnectionFailed(connectionResult: ConnectionResult): Unit =\n    wizardJobs.plusConnectionFailed(connectionResult).resolveAsync()\n\n  override def onDriveConnectionSuspended(cause: Int): Unit = {}\n\n  override def onDriveConnected(): Unit =\n    wizardJobs.driveConnected().resolveAsyncServiceOr(onException)\n\n  override def onDriveConnectionFailed(connectionResult: ConnectionResult): Unit =\n    wizardJobs.driveConnectionFailed(connectionResult).resolveAsync()\n\n  override def onClickOkMarketPermissionDialog(): Unit =\n    wizardJobs.requestAndroidMarketPermission().resolveAsyncServiceOr(onException)\n\n  override def onClickCancelMarketPermissionDialog(): Unit =\n    wizardJobs.visibilityUiActions.goToUser().resolveAsync()\n\n  override def onClickOkGooglePermissionDialog(): Unit =\n    wizardJobs.requestGooglePermission().resolveAsyncServiceOr(onException)\n\n  override def onClickCancelGooglePermissionDialog(): Unit =\n    wizardJobs.visibilityUiActions.goToUser().resolveAsync()\n\n  override def onClickOkSelectAccountsDialog(): Unit =\n    wizardJobs.connectAccount().resolveAsync()\n\n  override def onClickCancelSelectAccountsDialog(): Unit = {}\n\n  override def onClickOkPermissionsDialog(): Unit =\n    wizardJobs.requestPermissions().resolveAsync()\n\n  override def onClickCancelPermissionsDialog(): Unit =\n    wizardJobs.permissionDialogCancelled().resolveAsync()\n\n  override def onStartLoadConfiguration(cloudId: String): Unit =\n    (for {\n      client <- wizardJobs.googleDriveClient\n      _      <- loadConfigurationJobs.loadConfiguration(client, cloudId)\n      _      <- wizardJobs.wizardUiActions.showDiveIn()\n    } yield ()).resolveAsyncServiceOr(_ =>\n      wizardJobs.wizardUiActions.showErrorGeneral() *> wizardJobs.visibilityUiActions.goToUser())\n\n  override def onStartNewConfiguration(packages: Seq[PackagesByCategory]): Unit =\n    newConfigurationJobs.newConfigurationActions.loadFirstStep(packages).resolveAsync()\n\n  override def onLoadBetterCollections(packages: Seq[PackagesByCategory]): Unit =\n    newConfigurationJobs.loadBetterCollections(packages).resolveAsync()\n\n  override def onSaveCollections(collections: Seq[PackagesByCategory]): Unit =\n    newConfigurationJobs.saveCollections(collections).resolveAsyncServiceOr[Throwable] {\n      case ex: WizardNoCollectionsSelectedException =>\n        wizardJobs.wizardUiActions.showNoCollectionsSelectedMessage() *>\n          newConfigurationJobs.rollbackLoadBetterCollections()\n      case _ =>\n        wizardJobs.wizardUiActions.showErrorGeneral() *>\n          newConfigurationJobs.rollbackLoadBetterCollections()\n    }\n\n  override def onLoadMomentWithWifi(): Unit =\n    newConfigurationJobs.loadMomentWithWifi().resolveAsync()\n\n  override def onSaveMomentsWithWifi(infoMoment: Seq[(NineCardsMoment, Option[String])]): Unit =\n    newConfigurationJobs\n      .saveMomentsWithWifi(infoMoment)\n      .resolveAsyncServiceOr(_ => newConfigurationJobs.rollbackMomentWithWifi())\n\n  override def onSaveMoments(moments: Seq[NineCardsMoment]): Unit = {\n    (for {\n      _      <- newConfigurationJobs.saveMoments(moments)\n      client <- wizardJobs.googleDriveClient\n      _      <- synchronizeDeviceJobs.synchronizeDevice(client)\n      _      <- wizardJobs.visibilityUiActions.showNewConfiguration()\n      _      <- newConfigurationJobs.newConfigurationActions.loadSixthStep()\n    } yield ()).resolveAsyncServiceOr { _ =>\n      for {\n        _ <- wizardJobs.visibilityUiActions.cleanNewConfiguration()\n        _ <- wizardJobs.visibilityUiActions.showNewConfiguration()\n        _ <- newConfigurationJobs.newConfigurationActions.loadSixthStep()\n      } yield ()\n    }\n  }\n\n  private[this] def onException[E >: Throwable]: (E) => TaskService[Unit] = {\n    case ex: SocialProfileProcessException if ex.recoverable =>\n      wizardJobs.googleSignIn()\n    case _: UserException =>\n      wizardJobs.wizardUiActions.showErrorLoginUser() *> wizardJobs.visibilityUiActions.goToUser()\n    case _: UserV1Exception =>\n      wizardJobs.wizardUiActions.showErrorLoginUser() *> wizardJobs.visibilityUiActions.goToUser()\n    case _: WizardMarketTokenRequestCancelledException =>\n      wizardJobs.errorOperationMarketTokenCancelled()\n    case _: WizardGoogleTokenRequestCancelledException =>\n      wizardJobs.errorOperationGoogleTokenCancelled()\n    case _ =>\n      wizardJobs.wizardUiActions.showErrorConnectingGoogle() *> wizardJobs.visibilityUiActions\n        .goToUser()\n  }\n}\n\nobject WizardActivity {\n\n  def createWizardJobs(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]) = {\n    val dom = new WizardDOM(activityContextWrapper.getOriginal)\n    val listener =\n      activityContextWrapper.getOriginal.asInstanceOf[WizardUiListener]\n    new WizardJobs(\n      wizardUiActions = new WizardUiActions(dom, listener),\n      visibilityUiActions = new VisibilityUiActions(dom, listener))\n  }\n\n  def createNewConfigurationJobs(\n      implicit activityContextWrapper: ActivityContextWrapper,\n      fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n      uiContext: UiContext[_]) = {\n    val dom = new WizardDOM(activityContextWrapper.getOriginal)\n    val listener =\n      activityContextWrapper.getOriginal.asInstanceOf[WizardUiListener]\n    new NewConfigurationJobs(\n      wizardUiActions = new WizardUiActions(dom, listener),\n      newConfigurationActions = new NewConfigurationUiActions(dom, listener),\n      visibilityUiActions = new VisibilityUiActions(dom, listener))\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/wizard/WizardExceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.wizard\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class WizardMarketTokenRequestCancelledException(\n    message: String,\n    cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class WizardGoogleTokenRequestCancelledException(\n    message: String,\n    cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class WizardNoCollectionsSelectedException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/wizard/jobs/LoadConfigurationJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.wizard.jobs\n\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.ui.commons.{JobException, Jobs}\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.CloudStorageDevice\nimport com.google.android.gms.common.api.GoogleApiClient\nimport macroid.ActivityContextWrapper\n\nclass LoadConfigurationJobs(implicit contextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with Conversions {\n\n  def loadConfiguration(client: GoogleApiClient, cloudId: String): TaskService[Unit] = {\n\n    def loadConfiguration(device: CloudStorageDevice): TaskService[Unit] = {\n      for {\n        firebaseToken <- di.externalServicesProcess.readFirebaseToken\n          .map(token => Option(token))\n          .resolveLeftTo(None)\n        _ <- di.collectionProcess.createCollectionsFromCollectionData(\n          toSeqCollectionData(device.data.collections))\n        momentSeq  = device.data.moments map (_ map toMomentData) getOrElse Seq.empty\n        dockAppSeq = device.data.dockApps map (_ map toDockAppData) getOrElse Seq.empty\n        _ <- di.momentProcess.saveMoments(momentSeq)\n        _ <- di.deviceProcess.saveDockApps(dockAppSeq)\n        _ <- di.userProcess.updateUserDevice(device.data.deviceName, device.cloudId, firebaseToken)\n      } yield ()\n    }\n\n    for {\n      _      <- di.deviceProcess.resetSavedItems()\n      _      <- di.deviceProcess.synchronizeInstalledApps\n      device <- di.cloudStorageProcess.getCloudStorageDevice(client, cloudId)\n      _ <- if (device.data.collections.nonEmpty) {\n        loadConfiguration(device)\n      } else\n        TaskService.left(JobException(\"The device doesn't have collections\"))\n    } yield ()\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/wizard/jobs/NewConfigurationJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.wizard.jobs\n\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.ui.commons.Constants._\nimport cards.nine.app.ui.commons.Jobs\nimport cards.nine.app.ui.commons.ops.NineCardsCategoryOps._\nimport cards.nine.app.ui.wizard.WizardNoCollectionsSelectedException\nimport cards.nine.app.ui.wizard.jobs.uiactions.{\n  NewConfigurationUiActions,\n  VisibilityUiActions,\n  WizardUiActions\n}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.{TaskService, _}\nimport cards.nine.models.Moment.MomentTimeSlotOps\nimport cards.nine.models._\nimport cards.nine.models.types._\nimport macroid.ActivityContextWrapper\nimport monix.eval.Task\n\nclass NewConfigurationJobs(\n    val wizardUiActions: WizardUiActions,\n    val newConfigurationActions: NewConfigurationUiActions,\n    val visibilityUiActions: VisibilityUiActions)(implicit contextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with Conversions {\n\n  val defaultDockAppsSize = 4\n\n  val limitWidgets = 10\n\n  def loadBetterCollections(\n      packagesByCategory: Seq[PackagesByCategory] = Seq.empty): TaskService[Unit] =\n    for {\n      _ <- visibilityUiActions.hideFistStepAndShowLoadingBetterCollections(hidePrevious = true)\n      _ <- loadCollections(packagesByCategory)\n    } yield ()\n\n  def rollbackLoadBetterCollections(\n      packagesByCategory: Seq[PackagesByCategory] = Seq.empty): TaskService[Unit] =\n    for {\n      _ <- visibilityUiActions.hideFistStepAndShowLoadingBetterCollections(hidePrevious = false)\n      _ <- di.collectionProcess.cleanCollections()\n      _ <- loadCollections(packagesByCategory)\n    } yield ()\n\n  private[this] def loadCollections(\n      packagesByCategory: Seq[PackagesByCategory] = Seq.empty): TaskService[Unit] =\n    for {\n      _ <- di.deviceProcess.resetSavedItems()\n      _ <- di.deviceProcess.synchronizeInstalledApps\n      collections <- packagesByCategory match {\n        case Nil      => di.collectionProcess.rankApps()\n        case packages => TaskService.right(packages)\n      }\n      finalCollections = collections filter (collection => collection.category != Misc)\n      _ <- visibilityUiActions.showNewConfiguration()\n      _ <- newConfigurationActions.loadSecondStep(finalCollections)\n    } yield ()\n\n  def saveCollections(collections: Seq[PackagesByCategory]): TaskService[Unit] = {\n\n    def toCollectionData(apps: Seq[ApplicationData]) = {\n      collections.zipWithIndex.map { zipped =>\n        val (collection, index) = zipped\n        val packageNames        = collection.packages\n        val category            = collection.category\n        val collectionApps = packageNames flatMap (packageName =>\n                                                     apps.find(_.packageName == packageName))\n        val cards = collectionApps.zipWithIndex.map { zippedCard =>\n          val (app, indexApp) = zippedCard\n          CardData(\n            position = indexApp,\n            term = app.name,\n            packageName = Option(app.packageName),\n            cardType = AppCardType,\n            intent = toNineCardIntent(app))\n        }\n        CollectionData(\n          position = index,\n          name = category.getName,\n          collectionType = AppsCollectionType,\n          icon = collection.category.name.toLowerCase,\n          themedColorIndex = index % numSpaces,\n          cards = cards,\n          appsCategory = Option(category))\n      }\n    }\n\n    if (collections.isEmpty) {\n      TaskService.left(WizardNoCollectionsSelectedException(\"No collections selected\"))\n    } else {\n      for {\n        _    <- visibilityUiActions.hideSecondStepAndShowLoadingSavingCollection()\n        apps <- di.deviceProcess.getSavedApps(GetByName)\n        _    <- di.collectionProcess.createCollectionsFromCollectionData(toCollectionData(apps))\n        _    <- di.deviceProcess.generateDockApps(defaultDockAppsSize)\n        _    <- visibilityUiActions.showNewConfiguration()\n        _    <- newConfigurationActions.loadThirdStep()\n      } yield ()\n    }\n  }\n\n  def loadMomentWithWifi(): TaskService[Unit] =\n    for {\n      _ <- visibilityUiActions.hideThirdStep()\n      _ <- configureWithWifi()\n    } yield ()\n\n  def rollbackMomentWithWifi(): TaskService[Unit] =\n    for {\n      _ <- wizardUiActions.showErrorGeneral()\n      _ <- visibilityUiActions.cleanNewConfiguration()\n      _ <- di.momentProcess.deleteAllMoments()\n      _ <- di.widgetsProcess.deleteAllWidgets()\n      _ <- configureWithWifi()\n    } yield ()\n\n  private[this] def configureWithWifi(): TaskService[Unit] =\n    for {\n      wifis <- di.deviceProcess.getConfiguredNetworks\n      _     <- visibilityUiActions.showNewConfiguration()\n      _ <- newConfigurationActions.loadFourthStep(\n        wifis,\n        Seq((HomeMorningMoment, true), (WorkMoment, false), (StudyMoment, false)))\n    } yield ()\n\n  def saveMomentsWithWifi(infoMoment: Seq[(NineCardsMoment, Option[String])]): TaskService[Unit] = {\n    val homeNightMoment = infoMoment find (_._1 == HomeMorningMoment) map (info =>\n                                                                             (HomeNightMoment,\n                                                                              info._2))\n    val momentsToAdd: Seq[(NineCardsMoment, Option[String])] = (infoMoment :+ (NineCardsMoment.defaultMoment, None)) ++ Seq(\n        homeNightMoment).flatten\n\n    val momentsWithWifi = momentsToAdd map {\n      case (moment, wifi) =>\n        MomentData(\n          collectionId = None,\n          timeslot = moment.toMomentTimeSlot,\n          wifi = wifi.toSeq,\n          bluetooth = Seq.empty,\n          headphone = false,\n          momentType = moment)\n    }\n\n    for {\n      _            <- trackMomentTasks(momentsWithWifi)\n      _            <- visibilityUiActions.showLoadingSavingMoments()\n      momentsSaved <- di.momentProcess.saveMoments(momentsWithWifi)\n      nineCardsMoments = momentsSaved map (_.momentType)\n      widgetsByMoment <- di.collectionProcess.rankWidgetsByMoment(limitWidgets, nineCardsMoments)\n      _               <- di.widgetsProcess.addWidgets(toSeqWidgetData(momentsSaved, widgetsByMoment))\n      _               <- visibilityUiActions.showNewConfiguration()\n      _               <- newConfigurationActions.loadFifthStep()\n    } yield ()\n  }\n\n  def saveMoments(moments: Seq[NineCardsMoment]): TaskService[Unit] = {\n\n    val momentsWithoutWifi = moments map { moment =>\n      MomentData(\n        collectionId = None,\n        timeslot = moment.toMomentTimeSlot,\n        wifi = Seq.empty,\n        bluetooth = Seq.empty,\n        headphone = false,\n        momentType = moment)\n    }\n\n    for {\n      _            <- trackMomentTasks(momentsWithoutWifi)\n      _            <- visibilityUiActions.showLoadingSavingMoments()\n      momentsSaved <- di.momentProcess.saveMoments(momentsWithoutWifi)\n      nineCardsMoments = momentsSaved map (_.momentType)\n      widgetsByMoment <- di.collectionProcess.rankWidgetsByMoment(limitWidgets, nineCardsMoments)\n      _               <- di.widgetsProcess.addWidgets(toSeqWidgetData(momentsSaved, widgetsByMoment))\n    } yield ()\n  }\n\n  private[this] def trackMomentTasks(moments: Seq[MomentData]): TaskService[Unit] = {\n    val tasks = moments map { moment =>\n      (for {\n        _ <- di.trackEventProcess.chooseMoment(moment.momentType)\n        _ <- if (moment.wifi.nonEmpty)\n          di.trackEventProcess.chooseMomentWifi(moment.momentType)\n        else TaskService.empty\n      } yield ()).value\n    }\n    TaskService {\n      Task.gatherUnordered(tasks) map (_ => Right((): Unit))\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/wizard/jobs/WizardJobs.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.wizard.jobs\n\nimport android.accounts.AccountManager\nimport android.annotation.SuppressLint\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Build\nimport cards.nine.app.ui.commons.RequestCodes._\nimport cards.nine.app.ui.commons.SafeUi._\nimport cards.nine.app.ui.commons._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.preferences.commons.{GoogleDriveEmptyDeviceWizard, V1EmptyDeviceWizard}\nimport cards.nine.app.ui.wizard.jobs.uiactions.{VisibilityUiActions, WizardUiActions}\nimport cards.nine.app.ui.wizard.models.{\n  GoogleDriveDeviceType,\n  NoFoundDeviceType,\n  UserCloudDevices,\n  V1DeviceType\n}\nimport cards.nine.app.ui.wizard.{\n  WizardGoogleTokenRequestCancelledException,\n  WizardMarketTokenRequestCancelledException\n}\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.FineLocation\nimport cards.nine.models.{\n  CloudStorageDeviceData,\n  CloudStorageDeviceSummary,\n  PackagesByCategory,\n  UserV1Device\n}\nimport cards.nine.process.accounts.UserAccountsProcessOperationCancelledException\nimport cards.nine.process.cloud.Conversions\nimport cards.nine.process.userv1.UserV1ConfigurationException\nimport cats.data.EitherT\nimport cats.implicits._\nimport macroid.extras.DeviceVersion.Marshmallow\nimport macroid.extras.ResourcesExtras._\nimport com.fortysevendeg.ninecardslauncher.R\nimport com.google.android.gms.auth.api.Auth\nimport com.google.android.gms.common.api.GoogleApiClient\nimport com.google.android.gms.common.{AccountPicker, ConnectionResult, GoogleApiAvailability}\nimport macroid.{ActivityContextWrapper, Ui}\nimport monix.eval.Task\n\nimport scala.util.{Failure, Success, Try}\n\n/**\n * This class manages all jobs over the Wizard. This is the ideal flow:\n * - Activity calls to 'Job.initialize'\n * + Job calls to 'UiAction.initialize'\n * - UiAction calls to 'Job.connectAccount'\n * + Job starts a new activity for getting the account\n * - Activity calls to 'Job.activityResult'\n * + Job calls to 'Job.requestAndroidMarketPermission' that fetches the Android Market token\n * + Job calls to 'Job.requestGooglePermission' that fetches the Google Profile token\n * + Job calls to 'Job.tryToConnectDriveApiClient' that connects the Drive client\n * - GoogleDriveApiClientProvider calls 'Job.onDriveConnected'\n * + Job execute 'Job.googleSignIn'\n * + Job starts a new activity for Google Profile sign in\n * - Activity calls to 'Job.activityResult'\n * + Job fetches the tokenId and calls to 'Job.tryToConnectGoogleApiClient'\n * - GooglePlusApiClientProvider calls to 'Job.onPlusConnected'\n * + Job update the user profile information and calls to 'Job.loadDevices'\n * + Job calls to 'UiAction.showDevices' with the loaded devices\n * - UiAction calls to 'Job.deviceSelected'\n * + Job asks for Location permissions and calls to 'Job.generateCollections'\n * + Job starts the service\n * - Activity calls to 'Job.serviceFinished'\n * + Job calls to 'UiAction.showDiveIn'\n * - UiAction calls to 'Job.finishWizard'\n * + Job set the result RESULT_OK and finish the activity\n */\n@SuppressLint(Array(\"NewApi\"))\nclass WizardJobs(\n    val wizardUiActions: WizardUiActions,\n    val visibilityUiActions: VisibilityUiActions)(implicit contextWrapper: ActivityContextWrapper)\n    extends Jobs\n    with ImplicitsUiExceptions {\n\n  val accountType = \"com.google\"\n\n  val tagDialog = \"wizard-dialog\"\n\n  var clientStatuses = WizardJobsStatuses()\n\n  def initialize(): TaskService[Unit] =\n    for {\n      _ <- wizardUiActions.initialize()\n      _ <- visibilityUiActions.goToUser()\n    } yield ()\n\n  def stop(): TaskService[Unit] = {\n\n    def tryToClose(maybeClient: Option[GoogleApiClient]): Try[Unit] =\n      maybeClient map (c => Try(c.disconnect())) getOrElse Success((): Unit)\n\n    TaskService {\n      Task {\n        List(clientStatuses.driveApiClient, clientStatuses.plusApiClient) map tryToClose collect {\n          case Failure(e) => e\n        } match {\n          case Nil => Right((): Unit)\n          case list =>\n            val message =\n              s\"Error disconnecting clients:\\n ${list.map(_.getMessage).mkString(\" \\n\")}\"\n            Left(UiException(message, cause = None))\n        }\n      }\n    }\n  }\n\n  def connectAccount(): TaskService[Unit] = {\n    val resultCode =\n      GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(contextSupport.context)\n    if (resultCode == ConnectionResult.SUCCESS) {\n      val intent = Marshmallow ifSupportedThen {\n        AccountManager.newChooseAccountIntent(\n          javaNull,\n          javaNull,\n          Array(accountType),\n          javaNull,\n          javaNull,\n          javaNull,\n          javaNull)\n      } getOrElse {\n        AccountPicker.newChooseAccountIntent(\n          javaNull,\n          javaNull,\n          Array(accountType),\n          false,\n          javaNull,\n          javaNull,\n          javaNull,\n          javaNull)\n      }\n      for {\n        _ <- di.trackEventProcess.chooseAccount()\n        _ <- uiStartIntentForResult(intent, RequestCodes.selectAccount).toService()\n      } yield ()\n    } else {\n      onConnectionFailed(None, resultCode)\n    }\n  }\n\n  def showTermOfUseWebsite(): TaskService[Unit] =\n    di.launcherExecutorProcess.launchUrl(resGetString(R.string.web_tos))\n\n  def deviceSelected(packages: Seq[PackagesByCategory]): TaskService[Unit] =\n    for {\n      _              <- di.trackEventProcess.chooseNewConfiguration()\n      _              <- TaskService.right(clientStatuses = clientStatuses.copy(packages = packages))\n      havePermission <- di.userAccountsProcess.havePermission(FineLocation)\n      _ <- if (havePermission.result) generateCollections(None, packages)\n      else requestPermissions()\n    } yield ()\n\n  def deviceSelected(maybeKey: Option[String]): TaskService[Unit] =\n    for {\n      _ <- if (maybeKey.isEmpty) di.trackEventProcess.chooseNewConfiguration()\n      else di.trackEventProcess.chooseExistingDevice()\n      _              <- TaskService.right(clientStatuses = clientStatuses.copy(deviceKey = maybeKey))\n      havePermission <- di.userAccountsProcess.havePermission(FineLocation)\n      _ <- if (havePermission.result) generateCollections(maybeKey, Seq.empty)\n      else requestPermissions()\n    } yield ()\n\n  def requestPermissions(): TaskService[Unit] =\n    di.userAccountsProcess.requestPermission(RequestCodes.wizardPermissions, FineLocation)\n\n  def permissionDialogCancelled(): TaskService[Unit] =\n    generateCollections(clientStatuses.deviceKey, clientStatuses.packages)\n\n  def finishWizard(): TaskService[Unit] = TaskService {\n    CatchAll[UiException] {\n      activityContextSupport.getActivity match {\n        case Some(activity) =>\n          activity.setResult(Activity.RESULT_OK)\n          activity.finish()\n        case None =>\n          throw new NullPointerException(\"Activity instance not found\")\n      }\n    }\n  }\n\n  def activityResult(requestCode: Int, resultCode: Int, data: Intent): TaskService[Unit] =\n    (requestCode, resultCode) match {\n      case (RequestCodes.selectAccount, Activity.RESULT_OK) =>\n        clientStatuses =\n          clientStatuses.copy(email = readStringValue(data, AccountManager.KEY_ACCOUNT_NAME))\n        requestAndroidMarketPermission()\n      case (RequestCodes.selectAccount, _) =>\n        wizardUiActions.showSelectAccountDialog()\n      case (`resolveGooglePlayConnection`, Activity.RESULT_OK) =>\n        tryToConnectDriveApiClient()\n      case (`resolveGooglePlayConnection`, _) =>\n        wizardUiActions.showErrorConnectingGoogle() *> visibilityUiActions.goToUser()\n      case (`resolveConnectedUser`, Activity.RESULT_OK) =>\n        val mailTokenId = Option(Auth.GoogleSignInApi.getSignInResultFromIntent(data)) match {\n          case Some(result) if result.isSuccess =>\n            Option(result.getSignInAccount) flatMap (acct => Option(acct.getIdToken))\n          case _ => None\n        }\n        clientStatuses = clientStatuses.copy(mailTokenId = mailTokenId)\n        tryToConnectGoogleApiClient()\n      case (`resolveConnectedUser`, _) =>\n        wizardUiActions.showErrorConnectingGoogle() *> visibilityUiActions.goToUser()\n      case _ => TaskService(Task(Right((): Unit)))\n    }\n\n  def requestAndroidMarketPermission(): TaskService[Unit] = {\n\n    def invalidateToken(): TaskService[Unit] =\n      clientStatuses.androidMarketToken match {\n        case Some(token) => di.userAccountsProcess.invalidateToken(token)\n        case None        => TaskService(Task(Right((): Unit)))\n      }\n\n    def storeDriveApiClient(driveApiClient: GoogleApiClient): Unit =\n      clientStatuses = clientStatuses.copy(driveApiClient = Option(driveApiClient))\n\n    def storeAndroidMarketToken(token: String): Unit =\n      clientStatuses = clientStatuses.copy(androidMarketToken = Option(token))\n\n    clientStatuses.email match {\n      case Some(account) =>\n        for {\n          googleApiClient <- di.cloudStorageProcess.createCloudStorageClient(account)\n          _ = storeDriveApiClient(googleApiClient)\n          _ <- visibilityUiActions.showLoadingConnectingWithGoogle()\n          _ <- invalidateToken()\n          token <- di.userAccountsProcess\n            .getAuthToken(account, getString(R.string.android_market_oauth_scopes))\n            .resolveLeft {\n              case ex: UserAccountsProcessOperationCancelledException =>\n                Left(WizardMarketTokenRequestCancelledException(ex.getMessage, Some(ex)))\n              case ex: Throwable => Left(ex)\n            }\n          _ = storeAndroidMarketToken(token)\n          _ <- requestGooglePermission()\n        } yield ()\n      case None => visibilityUiActions.goToUser()\n    }\n\n  }\n\n  def requestGooglePermission(): TaskService[Unit] =\n    clientStatuses.email match {\n      case Some(account) =>\n        for {\n          _ <- visibilityUiActions.showLoadingRequestGooglePermission()\n          _ <- di.userAccountsProcess\n            .getAuthToken(account, getString(R.string.profile_and_drive_oauth_scopes))\n            .resolveLeft {\n              case ex: UserAccountsProcessOperationCancelledException =>\n                Left(WizardGoogleTokenRequestCancelledException(ex.getMessage, Some(ex)))\n              case ex: Throwable => Left(ex)\n            }\n          _ <- tryToConnectDriveApiClient()\n        } yield ()\n      case None => visibilityUiActions.goToUser()\n    }\n\n  def requestPermissionsResult(\n      requestCode: Int,\n      permissions: Array[String],\n      grantResults: Array[Int]): TaskService[Unit] = {\n\n    def generateOrRequest(hasPermission: Boolean, shouldRequest: Boolean): TaskService[Unit] =\n      if (hasPermission || !shouldRequest) {\n        generateCollections(clientStatuses.deviceKey, clientStatuses.packages)\n      } else {\n        wizardUiActions.showRequestPermissionsDialog()\n      }\n\n    if (requestCode == RequestCodes.wizardPermissions) {\n      for {\n        result        <- di.userAccountsProcess.parsePermissionsRequestResult(permissions, grantResults)\n        shouldRequest <- di.userAccountsProcess.shouldRequestPermission(FineLocation)\n        _             <- generateOrRequest(result.exists(_.hasPermission(FineLocation)), shouldRequest.result)\n      } yield ()\n    } else {\n      TaskService.empty\n    }\n  }\n\n  def errorOperationMarketTokenCancelled(): TaskService[Unit] =\n    wizardUiActions.showMarketPermissionDialog()\n\n  def errorOperationGoogleTokenCancelled(): TaskService[Unit] =\n    wizardUiActions.showGooglePermissionDialog()\n\n  def driveConnected(): TaskService[Unit] = googleSignIn()\n\n  def driveConnectionFailed(connectionResult: ConnectionResult): TaskService[Unit] =\n    onConnectionFailed(connectionResult)\n\n  def plusConnected(): TaskService[Unit] =\n    clientStatuses.plusApiClient match {\n      case Some(apiClient) =>\n        for {\n          _                <- visibilityUiActions.showLoadingConnectingWithGooglePlus()\n          maybeProfileName <- di.socialProfileProcess.updateUserProfile(apiClient)\n          _                <- loadDevices(maybeProfileName)\n        } yield ()\n      case None => visibilityUiActions.goToUser()\n    }\n\n  def plusConnectionFailed(connectionResult: ConnectionResult): TaskService[Unit] =\n    onConnectionFailed(connectionResult)\n\n  def googleSignIn(): TaskService[Unit] = {\n\n    def storePlusApiClient(plusApiClient: GoogleApiClient): Unit =\n      clientStatuses = clientStatuses.copy(plusApiClient = Option(plusApiClient))\n\n    def signInIntentService(plusApiClient: GoogleApiClient): TaskService[Unit] = {\n      val signInIntent = Auth.GoogleSignInApi.getSignInIntent(plusApiClient)\n      uiStartIntentForResult(signInIntent, resolveConnectedUser).toService()\n    }\n\n    clientStatuses.email match {\n      case Some(email) =>\n        for {\n          plusApiClient <- di.socialProfileProcess.createSocialProfileClient(\n            clientId = getString(R.string.api_v2_client_id),\n            account = email)\n          _ = storePlusApiClient(plusApiClient)\n          _ <- signInIntentService(plusApiClient)\n        } yield ()\n      case None => visibilityUiActions.goToUser()\n    }\n  }\n\n  def googleDriveClient: TaskService[GoogleApiClient] =\n    clientStatuses.driveApiClient match {\n      case Some(client) if client.isConnected => TaskService.right(client)\n      case Some(_)                            => TaskService.left(JobException(\"Client not connected\"))\n      case _                                  => TaskService.left(JobException(\"Client not available\"))\n    }\n\n  private[this] def onConnectionFailed(result: ConnectionResult): TaskService[Unit] = {\n    val maybeResult = Option(result)\n    val errorCode   = maybeResult.map(_.getErrorCode) getOrElse ConnectionResult.CANCELED\n    onConnectionFailed(maybeResult, errorCode)\n  }\n\n  private[this] def onConnectionFailed(\n      maybeResult: Option[ConnectionResult],\n      errorCode: Int): TaskService[Unit] = {\n\n    def showGoogleApiErrorDialog: TaskService[Unit] = withActivity { activity =>\n      Ui(\n        GoogleApiAvailability\n          .getInstance()\n          .getErrorDialog(activity, errorCode, resolveGooglePlayConnection)\n          .show()).toService()\n    }\n\n    def shouldShowDialog: Boolean =\n      errorCode == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED ||\n        errorCode == ConnectionResult.SERVICE_MISSING ||\n        errorCode == ConnectionResult.SERVICE_DISABLED\n\n    maybeResult match {\n      case Some(result) if result.hasResolution =>\n        withActivityTask { activity =>\n          result.startResolutionForResult(activity, resolveGooglePlayConnection)\n        }\n      case _ if shouldShowDialog =>\n        for {\n          _ <- visibilityUiActions.goToUser()\n          _ <- showGoogleApiErrorDialog\n        } yield ()\n      case _ =>\n        wizardUiActions.showErrorConnectingGoogle() *> visibilityUiActions.goToUser()\n    }\n  }\n\n  private[this] def generateCollections(\n      maybeKey: Option[String],\n      packages: Seq[PackagesByCategory]): TaskService[Unit] = {\n    (maybeKey, packages) match {\n      case (Some(key), _)       => visibilityUiActions.goToWizard(key)\n      case (_, p) if p.nonEmpty => visibilityUiActions.goToNewConfiguration(p)\n      case _                    => visibilityUiActions.goToNewConfiguration(Seq.empty)\n    }\n  }\n\n  private[this] def tryToConnectDriveApiClient(): TaskService[Unit] =\n    clientStatuses.driveApiClient match {\n      case Some(client) => TaskService(CatchAll[UiException](client.connect()))\n      case None         => requestAndroidMarketPermission()\n    }\n\n  private[this] def tryToConnectGoogleApiClient(): TaskService[Unit] =\n    clientStatuses.plusApiClient match {\n      case Some(client) =>\n        TaskService(CatchAll[UiException](client.connect(GoogleApiClient.SIGN_IN_MODE_OPTIONAL)))\n      case None => googleSignIn()\n    }\n\n  private[this] def loadDevices(maybeProfileName: Option[String]): TaskService[Unit] = {\n\n    // If we found some error when connecting to Backend V1 we just return an empty collection of devices\n    def loadDevicesFromV1(): TaskService[Seq[UserV1Device]] =\n      di.userV1Process\n        .getUserInfo(Build.MODEL, Seq(getString(R.string.android_market_oauth_scopes)))\n        .map(_.devices)\n        .resolveLeft {\n          case e: UserV1ConfigurationException =>\n            AppLog.info(\"Invalid configuration for backend V1\")\n            Right(Seq.empty)\n          case e => Right(Seq.empty)\n        }\n\n    def verifyAndUpdate(\n        client: GoogleApiClient,\n        email: String,\n        cloudStorageResources: Seq[CloudStorageDeviceSummary]): TaskService[UserCloudDevices] = {\n\n      import Conversions._\n\n      if (cloudStorageResources.isEmpty) {\n        for {\n          userInfoDevices <- loadDevicesFromV1()\n            .resolveIf(!V1EmptyDeviceWizard.readValue, Seq.empty)\n        } yield {\n          UserCloudDevices(\n            deviceType = V1DeviceType,\n            name = maybeProfileName getOrElse email,\n            userDevice = None,\n            devices = Seq.empty,\n            dataV1 = userInfoDevices)\n        }\n      } else {\n        for {\n          actualDevice <- di.cloudStorageProcess\n            .prepareForActualDevice(client, cloudStorageResources)\n          (maybeUserDevice, devices) = actualDevice\n        } yield {\n          UserCloudDevices(\n            deviceType = GoogleDriveDeviceType,\n            name = maybeProfileName getOrElse email,\n            userDevice = maybeUserDevice map toUserCloudDevice,\n            devices = devices map toUserCloudDevice,\n            dataV1 = Seq.empty)\n        }\n      }\n    }\n\n    def loadCloudDevices(\n        client: GoogleApiClient,\n        email: String,\n        androidMarketToken: String,\n        emailTokenId: String) = {\n      for {\n        _ <- di.userProcess.signIn(email, androidMarketToken, emailTokenId)\n        cloudStorageResources <- di.cloudStorageProcess\n          .getCloudStorageDevices(client)\n          .resolveIf(!GoogleDriveEmptyDeviceWizard.readValue, Seq.empty)\n        userCloudDevices <- verifyAndUpdate(client, email, cloudStorageResources).resolveLeftTo(\n          UserCloudDevices(NoFoundDeviceType, email, None, Seq.empty, Seq.empty))\n      } yield userCloudDevices\n\n    }\n\n    clientStatuses match {\n      case WizardJobsStatuses(\n          _,\n          _,\n          Some(client),\n          _,\n          Some(email),\n          Some(androidMarketToken),\n          Some(emailTokenId)) =>\n        for {\n          _       <- visibilityUiActions.showLoadingDevices()\n          devices <- loadCloudDevices(client, email, androidMarketToken, emailTokenId)\n          _ <- (devices.deviceType, devices.userDevice, devices.dataV1) match {\n            case (GoogleDriveDeviceType, Some(_), _) =>\n              wizardUiActions.showDevices(devices)\n            case (V1DeviceType, _, data) if data.nonEmpty =>\n              wizardUiActions.showDevices(devices)\n            case _ => deviceSelected(None)\n          }\n        } yield ()\n      case _ =>\n        wizardUiActions.showErrorConnectingGoogle() *> visibilityUiActions.goToUser()\n    }\n\n  }\n\n  protected def getString(res: Int): String = resGetString(res)\n\n}\n\ncase class WizardJobsStatuses(\n    deviceKey: Option[String] = None,\n    packages: Seq[PackagesByCategory] = Seq.empty,\n    driveApiClient: Option[GoogleApiClient] = None,\n    plusApiClient: Option[GoogleApiClient] = None,\n    email: Option[String] = None,\n    androidMarketToken: Option[String] = None,\n    mailTokenId: Option[String] = None)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/wizard/jobs/uiactions/NewConfigurationUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.wizard.jobs.uiactions\n\nimport android.support.v4.app.{DialogFragment, Fragment, FragmentManager}\nimport android.text.Html\nimport android.view.ViewGroup.LayoutParams._\nimport android.view.animation.DecelerateInterpolator\nimport android.view.{LayoutInflater, View}\nimport android.widget.LinearLayout.LayoutParams\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.{AppUtils, ImplicitsUiExceptions, SystemBarsTint, UiContext}\nimport cards.nine.app.ui.components.dialogs.WifiDialogFragment\nimport cards.nine.app.ui.components.drawables.{IconTypes, PathMorphDrawable}\nimport cards.nine.app.ui.components.widgets.tweaks.WizardCheckBoxTweaks._\nimport cards.nine.app.ui.components.widgets.tweaks.WizardMomentCheckBoxTweaks._\nimport cards.nine.app.ui.components.widgets.tweaks.WizardWifiCheckBoxTweaks._\nimport cards.nine.app.ui.components.widgets.{\n  WizardCheckBox,\n  WizardMomentCheckBox,\n  WizardWifiCheckBox\n}\nimport cards.nine.commons.javaNull\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.PackagesByCategory\nimport cards.nine.models.types._\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\n\nimport scala.concurrent.ExecutionContext.Implicits.global\n\nclass NewConfigurationUiActions(dom: WizardDOM, listener: WizardUiListener)(\n    implicit context: ActivityContextWrapper,\n    fragmentManagerContext: FragmentManagerContext[Fragment, FragmentManager],\n    uiContext: UiContext[_])\n    extends ImplicitsUiExceptions {\n\n  val numberOfScreens = 6\n\n  val numberPackageSelectedDefault = 3\n\n  val tagDialog = \"dialog\"\n\n  val padding = resGetDimensionPixelSize(R.dimen.padding_large)\n\n  lazy val defaultInterpolator = new DecelerateInterpolator(.7f)\n\n  lazy val systemBarsTint = new SystemBarsTint\n\n  lazy val iconNextDrawable = PathMorphDrawable(\n    defaultIcon = IconTypes.NEXT2,\n    defaultStroke = resGetDimensionPixelSize(R.dimen.stroke_thin),\n    padding = resGetDimensionPixelSize(R.dimen.padding_small))\n\n  val firstStep  = 0\n  val secondStep = 1\n  val thirdStep  = 2\n  val fourthStep = 3\n  val fifthStep  = 4\n\n  def loadFirstStep(packages: Seq[PackagesByCategory]): TaskService[Unit] = {\n    val stepView =\n      LayoutInflater.from(context.bestAvailable).inflate(R.layout.wizard_new_conf_step_0, javaNull)\n    val resColor = R.color.wizard_new_conf_accent_1\n    ((dom.newConfigurationStep <~\n      vgAddView(stepView)) ~\n      systemBarsTint.updateStatusColor(resGetColor(resColor)) ~\n      systemBarsTint.defaultStatusBar() ~\n      firstStepChoreographyIn ~\n      selectPager(firstStep) ~\n      (dom.newConfigurationNextIcon <~ ivSrc(iconNextDrawable)) ~\n      (dom.newConfigurationNextText <~ tvColorResource(resColor)) ~\n      (dom.newConfigurationNext <~\n        On.click(Ui(listener.onLoadBetterCollections(packages)))) ~\n      Ui(iconNextDrawable.setColor(resGetColor(resColor)))).toService()\n  }\n\n  def loadSecondStep(collections: Seq[PackagesByCategory]): TaskService[Unit] = {\n    val numberOfApps = collections.foldLeft(0)(_ + _.packages.size)\n    val stepView =\n      LayoutInflater.from(context.bestAvailable).inflate(R.layout.wizard_new_conf_step_1, javaNull)\n    val resColor = R.color.wizard_new_conf_accent_1\n    val description = resGetString(\n      R.string.wizard_new_conf_desc_step_1,\n      numberOfApps.toString,\n      collections.length.toString)\n    val collectionsSelectedDefault = collections count (_.packages.length > numberPackageSelectedDefault)\n    val counter = resGetString(\n      R.string.wizard_new_conf_collection_counter_step_1,\n      collectionsSelectedDefault.toString,\n      collections.length.toString)\n    val collectionViews = collections map { collection =>\n      (w[WizardCheckBox] <~\n        vWrapContent <~\n        wcbInitializeCollection(\n          collection,\n          collection.packages.length > numberPackageSelectedDefault) <~\n        FuncOn.click { view: View =>\n          val itemCheckBox = view.asInstanceOf[WizardCheckBox]\n          (itemCheckBox <~ wcbSwap()) ~\n            (dom.newConfigurationStep1AllCollections <~ wcbDoCheck(dom.areAllCollectionsChecked())) ~ {\n            val (checked, items) = dom.countCollectionsChecked()\n            val counter =\n              resGetString(\n                R.string.wizard_new_conf_collection_counter_step_1,\n                checked.toString,\n                items.toString)\n            dom.newConfigurationStep1CollectionCount <~ tvText(counter)\n          }\n        }).get\n    }\n\n    val params = new LayoutParams(MATCH_PARENT, WRAP_CONTENT)\n\n    ((dom.newConfigurationStep <~\n      vgAddView(stepView)) ~\n      selectPager(secondStep) ~\n      systemBarsTint.updateStatusColor(resGetColor(resColor)) ~\n      systemBarsTint.defaultStatusBar() ~\n      (dom.newConfigurationStep1AllCollections <~\n        wcbInitialize(\n          R.string.wizard_new_conf_collection_all_collections,\n          collections.length == collectionsSelectedDefault) <~\n        FuncOn.click { view: View =>\n          if (dom.areAllCollectionsChecked()) {\n            Ui.nop\n          } else {\n            val counter =\n              resGetString(\n                R.string.wizard_new_conf_collection_counter_step_1,\n                collections.length.toString,\n                collections.length.toString)\n            (view.asInstanceOf[WizardCheckBox] <~ wcbCheck()) ~\n              (dom.newConfigurationStep1CollectionCount <~ tvText(counter)) ~\n              checkAllCollections()\n          }\n        }) ~\n      (dom.newConfigurationStep1CollectionCount <~ tvText(counter)) ~\n      (dom.newConfigurationStep1CollectionsContent <~ vgAddViews(collectionViews, params)) ~\n      (dom.newConfigurationStep1Description <~ tvText(Html.fromHtml(description))) ~\n      (dom.newConfigurationStep <~~ applyFadeIn()) ~~\n      (dom.newConfigurationNextText <~ tvColorResource(resColor)) ~\n      (dom.newConfigurationNext <~\n        On.click(Ui(listener.onSaveCollections(dom.getCollectionsSelected)))) ~\n      Ui(iconNextDrawable.setColor(resGetColor(resColor)))).toService()\n  }\n\n  def loadThirdStep(): TaskService[Unit] = {\n    val stepView =\n      LayoutInflater.from(context.bestAvailable).inflate(R.layout.wizard_new_conf_step_2, javaNull)\n    val resColor = R.color.wizard_new_conf_accent_2\n    ((dom.newConfigurationStep <~\n      vgAddView(stepView)) ~\n      (dom.newConfigurationStep2Description <~ tvText(\n        Html.fromHtml(resGetString(R.string.wizard_new_conf_desc_step_2)))) ~\n      systemBarsTint.updateStatusColor(resGetColor(resColor)) ~\n      systemBarsTint.defaultStatusBar() ~\n      thirdStepChoreographyIn ~\n      selectPager(thirdStep) ~\n      (dom.newConfigurationNextText <~ tvColorResource(resColor)) ~\n      (dom.newConfigurationNext <~\n        On.click(Ui(listener.onLoadMomentWithWifi()))) ~\n      Ui(iconNextDrawable.setColor(resGetColor(resColor)))).toService()\n  }\n\n  def loadFourthStep(\n      wifis: Seq[String],\n      moments: Seq[(NineCardsMoment, Boolean)]): TaskService[Unit] = {\n    val stepView =\n      LayoutInflater.from(context.bestAvailable).inflate(R.layout.wizard_new_conf_step_3, javaNull)\n    val resColor = R.color.wizard_new_conf_accent_2\n    val momentViews = moments map {\n      case (moment, selected) =>\n        (w[WizardWifiCheckBox] <~\n          wwcbInitialize(moment, onWifiClick = () => {\n            val dialog = WifiDialogFragment(wifis, (wifi) => {\n              changeWifiName(moment, wifi).run\n            })(context, AppUtils.getDefaultTheme)\n            showDialog(dialog).run\n          }, selected) <~\n          FuncOn.click { view: View =>\n            val itemCheckBox = view.asInstanceOf[WizardWifiCheckBox]\n            itemCheckBox <~ wwcbSwap()\n          }).get\n    }\n    val params = new LayoutParams(MATCH_PARENT, WRAP_CONTENT)\n\n    ((dom.newConfigurationStep <~\n      vgAddView(stepView)) ~\n      systemBarsTint.updateStatusColor(resGetColor(resColor)) ~\n      systemBarsTint.defaultStatusBar() ~\n      selectPager(fourthStep) ~\n      (dom.newConfigurationStep3WifiContent <~ vgAddViews(momentViews, params)) ~\n      (dom.newConfigurationNextText <~ tvColorResource(resColor)) ~\n      (dom.newConfigurationNext <~\n        On.click(Ui(listener.onSaveMomentsWithWifi(dom.getWifisSelected)))) ~\n      Ui(iconNextDrawable.setColor(resGetColor(resColor)))).toService()\n  }\n\n  def loadFifthStep(): TaskService[Unit] = {\n\n    def momentTweak(moment: NineCardsMoment, defaultCheck: Boolean = true) =\n      wmcbInitialize(moment, defaultCheck) +\n        FuncOn.click { view: View =>\n          view.asInstanceOf[WizardMomentCheckBox] <~ wmcbSwap()\n        }\n\n    val stepView =\n      LayoutInflater.from(context.bestAvailable).inflate(R.layout.wizard_new_conf_step_4, javaNull)\n    val resColor = R.color.wizard_new_conf_accent_3\n    ((dom.newConfigurationStep <~\n      vgAddView(stepView)) ~\n      systemBarsTint.updateStatusColor(resGetColor(resColor)) ~\n      systemBarsTint.defaultStatusBar() ~\n      (dom.newConfigurationStep4Music <~\n        momentTweak(MusicMoment)) ~\n      (dom.newConfigurationStep4Car <~\n        momentTweak(CarMoment, defaultCheck = false)) ~\n      (dom.newConfigurationStep4Sport <~\n        momentTweak(SportMoment, defaultCheck = false)) ~\n      selectPager(fifthStep) ~\n      (dom.newConfigurationNextText <~ tvColorResource(resColor)) ~\n      (dom.newConfigurationNext <~\n        On.click(Ui(listener.onSaveMoments(dom.getMomentsSelected)))) ~\n      Ui(iconNextDrawable.setColor(resGetColor(resColor)))).toService()\n  }\n\n  def loadSixthStep(): TaskService[Unit] = {\n    val stepView =\n      LayoutInflater.from(context.bestAvailable).inflate(R.layout.wizard_new_conf_step_5, javaNull)\n    val resColor = R.color.wizard_new_conf_accent_4\n\n    ((dom.newConfigurationStep <~\n      vgAddView(stepView)) ~\n      systemBarsTint.updateStatusColor(resGetColor(resColor)) ~\n      systemBarsTint.defaultStatusBar() ~\n      (dom.newConfigurationStep5GoTo9Cards <~ On.click(Ui(listener.onClickFinishWizardButton()))) ~\n      sixthStepChoreographyIn ~\n      (dom.newConfigurationPagers <~ vGone) ~\n      (dom.newConfigurationNext <~ vGone)).toService()\n  }\n\n  private[this] def changeWifiName(moment: NineCardsMoment, wifi: String) =\n    dom.newConfigurationStep3WifiContent <~ Transformer {\n      case view: WizardWifiCheckBox if view.getMoment.contains(moment) =>\n        view <~ wwcbWifiName(wifi)\n    }\n\n  private[this] def checkAllCollections() =\n    dom.newConfigurationStep1CollectionsContent <~ Transformer {\n      case view: WizardCheckBox if !view.isCheck => view <~ wcbCheck()\n    }\n\n  private[this] def selectPager(position: Int): Ui[Any] =\n    dom.newConfigurationPagers <~ tvText(\n      resGetString(R.string.wizard_new_conf_steps_counter, (position + 1).toString))\n\n  private[this] def showDialog(dialog: DialogFragment) = Ui {\n    val ft = fragmentManagerContext.manager.beginTransaction()\n    Option(fragmentManagerContext.manager.findFragmentByTag(tagDialog)) foreach ft.remove\n    ft.addToBackStack(javaNull)\n    dialog.show(ft, tagDialog)\n  }\n\n  private[this] def firstStepChoreographyIn = {\n    (dom.newConfigurationStep0HeaderImage <~ vInvisible) ~\n      (dom.newConfigurationStep0Title <~ vInvisible) ~\n      (dom.newConfigurationStep0Description <~ vInvisible) ~\n      (dom.newConfigurationStep0HeaderContent <~\n        vPivotY(0) <~\n        vAlpha(0) <~\n        vScaleY(0) <~~\n        applyAnimation(\n          alpha = Some(1),\n          scaleY = Some(1),\n          interpolator = Some(defaultInterpolator))) ~~\n      (dom.newConfigurationStep0HeaderImage <~~ slideUp) ~~\n      (dom.newConfigurationStep0Title <~~ slideUp) ~~\n      (dom.newConfigurationStep0Description <~ slideUp)\n  }\n\n  private[this] def thirdStepChoreographyIn = {\n    (dom.newConfigurationStep2HeaderImage1 <~ vInvisible) ~\n      (dom.newConfigurationStep2HeaderImage2 <~ vInvisible) ~\n      (dom.newConfigurationStep2Title <~ vInvisible) ~\n      (dom.newConfigurationStep2Description <~ vInvisible) ~\n      (dom.newConfigurationStep2HeaderContent <~\n        vPivotY(0) <~\n        vAlpha(0) <~\n        vScaleY(0) <~~\n        applyAnimation(\n          alpha = Some(1),\n          scaleY = Some(1),\n          interpolator = Some(defaultInterpolator))) ~~\n      (dom.newConfigurationStep2HeaderImage1 <~~ slideLeft) ~~\n      (dom.newConfigurationStep2HeaderImage2 <~~ slideRight) ~~\n      (dom.newConfigurationStep2Title <~~ slideUp) ~~\n      (dom.newConfigurationStep2Description <~ slideUp)\n  }\n\n  private[this] def sixthStepChoreographyIn = {\n    (dom.newConfigurationStep5HeaderImage <~ vInvisible) ~\n      (dom.newConfigurationStep5Title <~ vInvisible) ~\n      (dom.newConfigurationStep5Description <~ vInvisible) ~\n      (dom.newConfigurationStep5GoTo9Cards <~ vInvisible) ~\n      (dom.newConfigurationStep5HeaderContent <~\n        vPivotY(0) <~\n        vAlpha(0) <~\n        vScaleY(0) <~~\n        applyAnimation(\n          alpha = Some(1),\n          scaleY = Some(1),\n          interpolator = Some(defaultInterpolator))) ~~\n      (dom.newConfigurationStep5HeaderImage <~~ slideUp) ~~\n      (dom.newConfigurationStep5Title <~~ slideUp) ~~\n      (dom.newConfigurationStep5Description <~ slideUp) ~~\n      (dom.newConfigurationStep5GoTo9Cards <~ slideUp)\n  }\n\n  private[this] def slideUp: Snail[View] =\n    vVisible + vAlpha(0) + vTranslationY(padding) ++ applyAnimation(\n      alpha = Some(1),\n      y = Some(0),\n      interpolator = Some(defaultInterpolator))\n\n  private[this] def slideLeft: Snail[View] =\n    vVisible + vAlpha(0) + vTranslationX(-padding) ++ applyAnimation(\n      alpha = Some(1),\n      x = Some(0),\n      interpolator = Some(defaultInterpolator))\n\n  private[this] def slideRight: Snail[View] =\n    vVisible + vAlpha(0) + vTranslationX(padding) ++ applyAnimation(\n      alpha = Some(1),\n      x = Some(0),\n      interpolator = Some(defaultInterpolator))\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/wizard/jobs/uiactions/VisibilityUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.wizard.jobs.uiactions\n\nimport android.view.View\nimport android.view.animation.DecelerateInterpolator\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.{SystemBarsTint, UiContext}\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.PackagesByCategory\nimport macroid.extras.ProgressBarTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid._\n\nimport scala.concurrent.ExecutionContext.Implicits.global\n\nclass VisibilityUiActions(dom: WizardDOM, listener: WizardUiListener)(\n    implicit val context: ActivityContextWrapper,\n    val uiContext: UiContext[_]) {\n\n  lazy val systemBarsTint = new SystemBarsTint\n\n  lazy val defaultInterpolator = new DecelerateInterpolator(.7f)\n\n  val translate = resGetDimensionPixelSize(R.dimen.padding_xlarge)\n\n  def goToUser(): TaskService[Unit] = {\n\n    def applyAnim()(implicit context: ContextWrapper): Snail[View] =\n      vVisible +\n        vAlpha(0) +\n        vTranslationY(-translate) ++\n        applyAnimation(\n          alpha = Option(1),\n          y = Option(0),\n          duration = Option(resGetInteger(R.integer.wizard_anim_ripple_duration)))\n\n    ((dom.loadingRootLayout <~ vInvisible) ~\n      (dom.userRootLayout <~ vVisible) ~\n      (dom.userLogo <~ vInvisible) ~\n      (dom.userTitle <~ vInvisible) ~\n      (dom.userAction <~ vInvisible) ~\n      (dom.usersTerms <~ vInvisible) ~\n      (dom.wizardRootLayout <~ vInvisible) ~\n      (dom.deviceRootLayout <~ vInvisible) ~\n      (dom.newConfigurationContent <~ vInvisible) ~\n      (dom.userLogo <~~ applyAnim()) ~~\n      (dom.userTitle <~~ applyAnim()) ~~\n      (dom.userAction <~~ applyAnim()) ~~\n      (dom.usersTerms <~~ applyAnim())).toService()\n  }\n\n  def goToWizard(cloudId: String): TaskService[Unit] = {\n    val backgroundColor = resGetColor(R.color.wizard_background_step_1)\n    ((dom.loadingRootLayout <~ vInvisible) ~\n      (dom.userRootLayout <~ vInvisible) ~\n      systemBarsTint.updateStatusColor(backgroundColor) ~\n      systemBarsTint.defaultStatusBar() ~\n      (dom.deviceRootLayout <~ vInvisible) ~\n      (dom.newConfigurationContent <~ vInvisible) ~\n      (dom.workspaces <~ vInvisible) ~\n      (dom.wizardRootLayout <~ vVisible) ~\n      (dom.stepsBackground <~\n        vBackgroundColor(backgroundColor) <~\n        vPivotY(0) <~\n        vScaleY(0) <~~\n        applyAnimation(scaleY = Some(1))) ~~\n      (dom.workspaces <~~ applyFadeIn()) ~\n      Ui(listener.onStartLoadConfiguration(cloudId))).toService()\n  }\n\n  def goToNewConfiguration(packages: Seq[PackagesByCategory]): TaskService[Unit] =\n    (showNewConfigurationScreen() ~\n      Ui(listener.onStartNewConfiguration(packages))).toService()\n\n  def showNewConfiguration(): TaskService[Unit] =\n    showNewConfigurationScreen().toService()\n\n  def showLoadingConnectingWithGoogle(): TaskService[Unit] =\n    showLoading(R.string.wizard_loading_connecting_with_google).toService()\n\n  def showLoadingRequestGooglePermission(): TaskService[Unit] =\n    showLoading(R.string.wizard_loading_request_google_permission).toService()\n\n  def showLoadingConnectingWithGooglePlus(): TaskService[Unit] =\n    showLoading(R.string.wizard_loading_connecting_with_plus).toService()\n\n  def hideFistStepAndShowLoadingBetterCollections(hidePrevious: Boolean): TaskService[Unit] =\n    ((dom.newConfigurationNext <~ vClickable(false)) ~\n      firstStepChoreographyOut.ifUi(hidePrevious) ~\n      showLoading(R.string.wizard_loading_looking_for_better_collection) ~\n      updateStatusColor() ~\n      (dom.loadingBar <~ pbColor(resGetColor(R.color.wizard_new_conf_accent_1)))).toService()\n\n  def hideSecondStepAndShowLoadingSavingCollection(): TaskService[Unit] =\n    ((dom.newConfigurationNext <~ vClickable(false)) ~\n      secondStepChoreographyOut ~\n      showLoading(R.string.wizard_loading_saving_collections) ~\n      updateStatusColor() ~\n      (dom.loadingBar <~ pbColor(resGetColor(R.color.wizard_new_conf_accent_2)))).toService()\n\n  def hideThirdStep(): TaskService[Unit] = thirdStepChoreographyOut.toService()\n\n  def cleanNewConfiguration(): TaskService[Unit] =\n    (dom.newConfigurationStep <~ vgRemoveAllViews).toService()\n\n  def showLoadingSavingMoments(): TaskService[Unit] =\n    (showLoading(R.string.wizard_loading_saving_moments) ~\n      updateStatusColor() ~\n      (dom.loadingBar <~ pbColor(resGetColor(R.color.wizard_new_conf_accent_3)))).toService()\n\n  def showLoadingDevices(): TaskService[Unit] =\n    showLoading(R.string.wizard_loading_devices).toService()\n\n  private[this] def updateStatusColor(): Ui[Any] =\n    systemBarsTint.lightStatusBar() ~ systemBarsTint.updateStatusColor(\n      resGetColor(R.color.background_app))\n\n  private[this] def showLoading(resText: Int, colorBar: Option[Int] = None): Ui[Any] =\n    (dom.loadingRootLayout <~ vVisible) ~\n      (dom.loadingText <~ tvText(resText)) ~\n      (dom.userRootLayout <~ vInvisible) ~\n      (dom.wizardRootLayout <~ vInvisible) ~\n      (dom.deviceRootLayout <~ vInvisible) ~\n      (dom.newConfigurationContent <~ vInvisible) ~\n      (dom.newConfigurationNext <~ vClickable(true)) ~\n      (dom.newConfigurationStep <~ vgRemoveAllViews)\n\n  private[this] def showNewConfigurationScreen(): Ui[Any] =\n    (dom.loadingRootLayout <~ vInvisible) ~\n      (dom.userRootLayout <~ vInvisible) ~\n      (dom.wizardRootLayout <~ vInvisible) ~\n      (dom.deviceRootLayout <~ vInvisible) ~\n      (dom.newConfigurationContent <~ vVisible)\n\n  private[this] def firstStepChoreographyOut = {\n    (dom.newConfigurationStep0HeaderContent <~\n      vPivotY(0) <~\n      applyAnimation(alpha = Some(0), scaleY = Some(0), interpolator = Some(defaultInterpolator))) ~\n      (dom.newConfigurationStep0HeaderImage <~ applyFadeOut()) ~\n      (dom.newConfigurationStep0Title <~ applyFadeOut()) ~\n      (dom.newConfigurationStep0Description <~ applyFadeOut())\n  }\n\n  private[this] def secondStepChoreographyOut = {\n    (dom.newConfigurationStep1Title <~ applyFadeOut()) ~\n      (dom.newConfigurationStep1Description <~ applyFadeOut()) ~\n      (dom.newConfigurationStep1AllCollections <~ applyFadeOut()) ~\n      (dom.newConfigurationStep1CollectionCount <~ applyFadeOut()) ~\n      (dom.newConfigurationStep1CollectionsContent <~ applyFadeOut())\n  }\n\n  private[this] def thirdStepChoreographyOut = {\n    (dom.newConfigurationStep2HeaderContent <~\n      vPivotY(0) <~\n      applyAnimation(alpha = Some(0), scaleY = Some(0), interpolator = Some(defaultInterpolator))) ~\n      (dom.newConfigurationStep2HeaderImage1 <~ applyFadeOut()) ~\n      (dom.newConfigurationStep2HeaderImage2 <~ applyFadeOut()) ~\n      (dom.newConfigurationStep2Title <~ applyFadeOut()) ~\n      (dom.newConfigurationStep2Description <~~ applyFadeOut())\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/wizard/jobs/uiactions/WizardDOM.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.wizard.jobs.uiactions\n\nimport android.app.Activity\nimport cards.nine.app.ui.commons.ActivityFindViews\nimport cards.nine.app.ui.components.widgets.{WizardCheckBox, WizardWifiCheckBox}\nimport cards.nine.models.PackagesByCategory\nimport cards.nine.models.types.NineCardsMoment\nimport com.fortysevendeg.ninecardslauncher.TR\n\nclass WizardDOM(activity: Activity) {\n\n  import ActivityFindViews._\n\n  lazy val rootLayout = findView(TR.wizard_root).run(activity)\n\n  lazy val loadingRootLayout =\n    findView(TR.wizard_loading_content).run(activity)\n\n  lazy val loadingBar = findView(TR.wizard_loading_bar).run(activity)\n\n  lazy val loadingText = findView(TR.wizard_loading_text).run(activity)\n\n  lazy val userRootLayout = findView(TR.wizard_user_content).run(activity)\n\n  lazy val usersTerms = findView(TR.wizard_user_terms).run(activity)\n\n  lazy val userLogo = findView(TR.wizard_user_logo).run(activity)\n\n  lazy val userTitle = findView(TR.wizard_user_title).run(activity)\n\n  lazy val userAction = findView(TR.wizard_user_action).run(activity)\n\n  lazy val titleDevice = findView(TR.wizard_device_title).run(activity)\n\n  lazy val deviceRootLayout = findView(TR.wizard_device_content).run(activity)\n\n  lazy val devicesGroup = findView(TR.wizard_device_group).run(activity)\n\n  lazy val deviceAction = findView(TR.wizard_device_action).run(activity)\n\n  lazy val stepsBackground = findView(TR.wizard_steps_background).run(activity)\n\n  lazy val stepsAction = findView(TR.wizard_steps_action).run(activity)\n\n  lazy val stepsDownloadingMessage =\n    findView(TR.wizard_steps_downloading_message).run(activity)\n\n  lazy val wizardRootLayout = findView(TR.wizard_steps_content).run(activity)\n\n  lazy val paginationPanel =\n    findView(TR.wizard_steps_pagination_panel).run(activity)\n\n  lazy val workspaces = findView(TR.wizard_steps_workspace).run(activity)\n\n  lazy val newConfigurationContent =\n    findView(TR.wizard_steps_new_configuration_content).run(activity)\n\n  lazy val newConfigurationStep =\n    findView(TR.wizard_steps_new_configuration_step).run(activity)\n\n  lazy val newConfigurationPagers =\n    findView(TR.wizard_steps_new_configuration_pager).run(activity)\n\n  lazy val newConfigurationNext =\n    findView(TR.wizard_steps_new_configuration_next).run(activity)\n\n  lazy val newConfigurationNextText =\n    findView(TR.wizard_steps_new_configuration_next_text).run(activity)\n\n  lazy val newConfigurationNextIcon =\n    findView(TR.wizard_steps_new_configuration_next_icon).run(activity)\n\n  def newConfigurationStep0HeaderContent =\n    findView(TR.wizard_steps_new_configuration_step0_header_content).run(activity)\n\n  def newConfigurationStep0HeaderImage =\n    findView(TR.wizard_steps_new_configuration_step0_header_image).run(activity)\n\n  def newConfigurationStep0Title =\n    findView(TR.wizard_steps_new_configuration_step0_title).run(activity)\n\n  def newConfigurationStep0Description =\n    findView(TR.wizard_steps_new_configuration_step0_description).run(activity)\n\n  def newConfigurationStep1Title =\n    findView(TR.wizard_steps_new_configuration_step1_title).run(activity)\n\n  def newConfigurationStep1Description =\n    findView(TR.wizard_steps_new_configuration_step1_description).run(activity)\n\n  def newConfigurationStep1AllCollections =\n    findView(TR.wizard_steps_new_configuration_step1_all_collections).run(activity)\n\n  def newConfigurationStep1CollectionCount =\n    findView(TR.wizard_steps_new_configuration_step1_collection_count).run(activity)\n\n  def newConfigurationStep1CollectionsContent =\n    findView(TR.wizard_steps_new_configuration_step1_collection_content).run(activity)\n\n  def newConfigurationStep2HeaderContent =\n    findView(TR.wizard_steps_new_configuration_step2_header_content).run(activity)\n\n  def newConfigurationStep2HeaderImage1 =\n    findView(TR.wizard_steps_new_configuration_step2_header_image1).run(activity)\n\n  def newConfigurationStep2HeaderImage2 =\n    findView(TR.wizard_steps_new_configuration_step2_header_image2).run(activity)\n\n  def newConfigurationStep2Title =\n    findView(TR.wizard_steps_new_configuration_step2_title).run(activity)\n\n  def newConfigurationStep2Description =\n    findView(TR.wizard_steps_new_configuration_step2_description).run(activity)\n\n  def newConfigurationStep3WifiContent =\n    findView(TR.wizard_steps_new_configuration_step3_wifi_content).run(activity)\n\n  def newConfigurationStep4Music =\n    findView(TR.wizard_moment_step4_music).run(activity)\n\n  def newConfigurationStep4Car =\n    findView(TR.wizard_moment_step4_car).run(activity)\n\n  def newConfigurationStep4Sport =\n    findView(TR.wizard_moment_step4_sport).run(activity)\n\n  def newConfigurationStep5HeaderContent =\n    findView(TR.wizard_steps_new_configuration_step5_header_content).run(activity)\n\n  def newConfigurationStep5HeaderImage =\n    findView(TR.wizard_steps_new_configuration_step5_header_image).run(activity)\n\n  def newConfigurationStep5Title =\n    findView(TR.wizard_steps_new_configuration_step5_title).run(activity)\n\n  def newConfigurationStep5Description =\n    findView(TR.wizard_steps_new_configuration_step5_description).run(activity)\n\n  def newConfigurationStep5GoTo9Cards =\n    findView(TR.wizard_moment_step5_go_to_9cards).run(activity)\n\n  def getWizardCheckBoxes: Seq[WizardCheckBox] =\n    (0 to newConfigurationStep1CollectionsContent.getChildCount) flatMap { position =>\n      newConfigurationStep1CollectionsContent.getChildAt(position) match {\n        case widget: WizardCheckBox => Some(widget)\n        case _                      => None\n      }\n    }\n\n  def getWizardWifiCheckBoxes: Seq[WizardWifiCheckBox] =\n    (0 to newConfigurationStep3WifiContent.getChildCount) flatMap { position =>\n      newConfigurationStep3WifiContent.getChildAt(position) match {\n        case widget: WizardWifiCheckBox => Some(widget)\n        case _                          => None\n      }\n    }\n\n  def areAllCollectionsChecked(): Boolean =\n    getWizardCheckBoxes forall (_.isCheck)\n\n  def countCollectionsChecked(): (Int, Int) = {\n    val items = getWizardCheckBoxes\n    (items count (_.isCheck), items.length)\n  }\n\n  def getCollectionsSelected: Seq[PackagesByCategory] =\n    getWizardCheckBoxes flatMap (_.getDataIfSelected)\n\n  def getWifisSelected: Seq[(NineCardsMoment, Option[String])] =\n    getWizardWifiCheckBoxes flatMap (widget =>\n                                       (widget.isCheck, widget.getMoment, widget.getWifiName) match {\n                                         case (true, Some(moment), wifiName) =>\n                                           Option(moment, wifiName)\n                                         case _ => None\n                                       })\n\n  def getMomentsSelected: Seq[NineCardsMoment] =\n    Seq(\n      newConfigurationStep4Music.getMomentIfSelected,\n      newConfigurationStep4Car.getMomentIfSelected,\n      newConfigurationStep4Sport.getMomentIfSelected).flatten\n\n}\n\ntrait WizardUiListener {\n\n  def onClickAcceptTermsButton(): Unit\n\n  def onClickVisitTermsButton(): Unit\n\n  def onClickSelectV1DeviceButton(packages: Seq[PackagesByCategory]): Unit\n\n  def onClickSelectDeviceButton(maybeCloudId: Option[String]): Unit\n\n  def onClickFinishWizardButton(): Unit\n\n  def onClickOkMarketPermissionDialog(): Unit\n\n  def onClickCancelMarketPermissionDialog(): Unit\n\n  def onClickOkGooglePermissionDialog(): Unit\n\n  def onClickCancelGooglePermissionDialog(): Unit\n\n  def onClickOkSelectAccountsDialog(): Unit\n\n  def onClickCancelSelectAccountsDialog(): Unit\n\n  def onClickOkPermissionsDialog(): Unit\n\n  def onClickCancelPermissionsDialog(): Unit\n\n  def onStartLoadConfiguration(cloudId: String): Unit\n\n  def onStartNewConfiguration(packages: Seq[PackagesByCategory]): Unit\n\n  def onLoadBetterCollections(packages: Seq[PackagesByCategory]): Unit\n\n  def onSaveCollections(collections: Seq[PackagesByCategory]): Unit\n\n  def onLoadMomentWithWifi(): Unit\n\n  def onSaveMomentsWithWifi(infoMoment: Seq[(NineCardsMoment, Option[String])]): Unit\n\n  def onSaveMoments(moments: Seq[NineCardsMoment]): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/wizard/jobs/uiactions/WizardUiActions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.wizard.jobs.uiactions\n\nimport android.os.Build\nimport android.support.v7.app.AppCompatActivity\nimport android.text.Html\nimport android.view.{Gravity, View, ViewGroup}\nimport android.widget._\nimport cards.nine.app.ui.commons.CommonsExcerpt._\nimport macroid.extras.UIActionsExtras._\nimport cards.nine.app.ui.commons.SnailsCommons._\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.commons.{ImplicitsUiExceptions, SystemBarsTint, UiContext, UiException}\nimport cards.nine.app.ui.components.dialogs.AlertDialogFragment\nimport cards.nine.app.ui.components.layouts.StepData\nimport cards.nine.app.ui.components.layouts.tweaks.AnimatedWorkSpacesTweaks._\nimport cards.nine.app.ui.components.layouts.tweaks.StepsWorkspacesTweaks._\nimport cards.nine.app.ui.wizard.models._\nimport cards.nine.commons._\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{NineCardsIntentConversions, PackagesByCategory}\nimport cards.nine.models.types.{AppCardType, Misc}\nimport macroid.extras.ImageViewTweaks._\nimport macroid.extras.LinearLayoutTweaks._\nimport macroid.extras.ResourcesExtras._\nimport macroid.extras.TextViewTweaks._\nimport macroid.extras.ViewGroupTweaks._\nimport macroid.extras.ViewTweaks._\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.FullDsl._\nimport macroid._\nimport org.ocpsoft.prettytime.PrettyTime\n\nimport scala.util.Try\n\nclass WizardUiActions(dom: WizardDOM, listener: WizardUiListener)(\n    implicit val context: ActivityContextWrapper,\n    val uiContext: UiContext[_])\n    extends ImplicitsUiExceptions\n    with NineCardsIntentConversions {\n\n  val newConfigurationKey = \"new_configuration\"\n\n  val tagDialog = \"wizard-dialog\"\n\n  lazy val systemBarsTint = new SystemBarsTint\n\n  lazy val steps = Seq(\n    StepData(\n      R.drawable.wizard_01,\n      resGetColor(R.color.wizard_background_step_1),\n      resGetString(R.string.wizard_step_title_1),\n      resGetString(R.string.wizard_step_1)),\n    StepData(\n      R.drawable.wizard_02,\n      resGetColor(R.color.wizard_background_step_2),\n      resGetString(R.string.wizard_step_title_2),\n      resGetString(R.string.wizard_step_2)),\n    StepData(\n      R.drawable.wizard_03,\n      resGetColor(R.color.wizard_background_step_3),\n      resGetString(R.string.wizard_step_title_3),\n      resGetString(R.string.wizard_step_3)),\n    StepData(\n      R.drawable.wizard_04,\n      resGetColor(R.color.wizard_background_step_4),\n      resGetString(R.string.wizard_step_title_4),\n      resGetString(R.string.wizard_step_4)),\n    StepData(\n      R.drawable.wizard_05,\n      resGetColor(R.color.wizard_background_step_5),\n      resGetString(R.string.wizard_step_title_5),\n      resGetString(R.string.wizard_step_5)))\n\n  def initialize(): TaskService[Unit] = {\n\n    def pagination(position: Int) =\n      (w[ImageView] <~ paginationItemStyle <~ ivSrc(R.drawable.wizard_pager) <~ vTag(\n        position.toString)).get\n\n    def createPagers(steps: Seq[StepData]) = {\n      val pagerViews = steps.indices map { position =>\n        val view = pagination(position)\n        view.setActivated(position == 0)\n        view\n      }\n      dom.paginationPanel <~ vgAddViews(pagerViews)\n    }\n\n    def reloadPagers(currentPage: Int) = Transformer {\n      case i: ImageView if Option(i.getTag).isDefined && i.getTag.equals(currentPage.toString) =>\n        i <~ vActivated(true)\n      case i: ImageView => i <~ vActivated(false)\n    }\n\n    ((dom.userAction <~\n      defaultActionStyle <~\n      On.click(Ui(listener.onClickAcceptTermsButton()))) ~\n      (dom.deviceAction <~\n        defaultActionStyle) ~\n      (dom.workspaces <~\n        vGlobalLayoutListener(_ => {\n          dom.workspaces <~\n            swData(steps) <~\n            swAddMovementObserver((current, go, isLeft, fraction) => {\n              val color = (current.color, go.color).interpolateColors(fraction)\n              ((dom.stepsBackground <~ vBackgroundColor(color)) ~ systemBarsTint.updateStatusColor(\n                color)).run\n            }) <~\n            awsAddPageChangedObserver(currentPage => {\n              val showAction = currentPage == steps.length - 1\n              ((dom.paginationPanel <~ reloadPagers(currentPage)) ~\n                ((showAction,\n                  (dom.stepsAction ~> isVisible).get,\n                  (dom.paginationPanel ~> isVisible).get) match {\n                  case (true, false, _) =>\n                    (dom.stepsAction <~ applyFadeIn()) ~\n                      (dom.paginationPanel <~ applyFadeOut()) ~\n                      (dom.stepsDownloadingMessage <~ (if ((dom.stepsAction ~> isEnabled).get)\n                                                         vGone\n                                                       else vVisible))\n                  case (false, _, false) =>\n                    (dom.stepsAction <~ applyFadeOut()) ~\n                      (dom.paginationPanel <~ applyFadeIn()) ~\n                      (dom.stepsDownloadingMessage <~ vGone)\n                  case _ => Ui.nop\n                })).run\n            })\n        })) ~\n      (dom.userTitle <~ tvText(Html.fromHtml(resGetString(R.string.welcome)))) ~\n      (dom.usersTerms <~\n        tvText(Html.fromHtml(resGetString(R.string.termsAndConditions))) <~\n        On.click(Ui(listener.onClickVisitTermsButton()))) ~\n      (dom.stepsAction <~\n        diveInActionStyle <~\n        On.click(Ui(listener.onClickFinishWizardButton()))) ~\n      createPagers(steps) ~\n      systemBarsTint.initSystemStatusBarTint() ~\n      systemBarsTint.updateStatusColor(resGetColor(R.color.background_app)) ~\n      systemBarsTint.lightStatusBar()).toService()\n  }\n\n  def showErrorLoginUser(): TaskService[Unit] =\n    uiShortToast(R.string.errorLoginUser).toService()\n\n  def showErrorGeneral(): TaskService[Unit] =\n    uiShortToast(R.string.contactUsError).toService()\n\n  def showNoCollectionsSelectedMessage(): TaskService[Unit] =\n    uiShortToast(R.string.errorNoCollectionsSelected).toService()\n\n  def showErrorConnectingGoogle(): TaskService[Unit] =\n    uiShortToast(R.string.errorConnectingGoogle).toService()\n\n  def showDevices(devices: UserCloudDevices): TaskService[Unit] = {\n\n    def devicesChecked() = Transformer {\n      case i: RadioButton if i.isChecked =>\n        Ui {\n          val tag = Option(i.getTag) map (_.toString)\n          (tag, devices.deviceType) match {\n            case (Some(`newConfigurationKey`), _) =>\n              listener.onClickSelectDeviceButton(None)\n            case (cloudId, GoogleDriveDeviceType) =>\n              listener.onClickSelectDeviceButton(cloudId)\n            case (deviceId, V1DeviceType) =>\n              val packages = devices.dataV1 find (data => deviceId.contains(data.deviceId)) map {\n                device =>\n                  device.collections flatMap { collection =>\n                    val packages = collection.items filter (_.itemType == AppCardType) flatMap {\n                      p =>\n                        Try(jsonToNineCardIntent(p.intent)).toOption flatMap (_.extractPackageName())\n                    }\n                    if (packages.isEmpty) {\n                      None\n                    } else {\n                      Option(\n                        PackagesByCategory(\n                          category = collection.category getOrElse Misc,\n                          packages = packages\n                        ))\n                    }\n                  }\n              }\n              packages match {\n                case Some(p) if p.nonEmpty =>\n                  listener.onClickSelectV1DeviceButton(p)\n                case _ => listener.onClickSelectDeviceButton(None)\n              }\n            case _ => listener.onClickSelectDeviceButton(None)\n          }\n        }\n    }\n\n    def showDevices(): Ui[Any] =\n      (dom.loadingRootLayout <~ vGone) ~\n        (dom.userRootLayout <~ vGone) ~\n        (dom.wizardRootLayout <~ vGone) ~\n        (dom.deviceRootLayout <~ vVisible) ~\n        (dom.deviceAction <~ On.click(dom.devicesGroup <~ devicesChecked()))\n\n    for {\n      _ <- (devices.deviceType match {\n        case V1DeviceType => addV1DevicesToRadioGroup(devices)\n        case GoogleDriveDeviceType =>\n          addGoogleDriveDevicesToRadioGroup(devices)\n        case NoFoundDeviceType => Ui.nop\n      }).toService()\n      _ <- showDevices().toService()\n      _ <- (dom.titleDevice <~ tvText(resGetString(R.string.addDeviceTitle, devices.name)))\n        .toService()\n    } yield ()\n  }\n\n  def showDiveIn(): TaskService[Unit] =\n    ((dom.stepsDownloadingMessage <~ vGone) ~\n      (dom.stepsAction <~\n        vEnabled(true) <~\n        vBackgroundTint(resGetColor(R.color.wizard_background_action_enable)))).toService()\n\n  def showMarketPermissionDialog(): TaskService[Unit] =\n    showErrorDialog(\n      message = R.string.errorAndroidMarketPermissionNotAccepted,\n      action = listener.onClickOkMarketPermissionDialog,\n      negativeAction = listener.onClickCancelMarketPermissionDialog)\n\n  def showGooglePermissionDialog(): TaskService[Unit] =\n    showErrorDialog(\n      message = R.string.errorGooglePermissionNotAccepted,\n      action = listener.onClickOkGooglePermissionDialog,\n      negativeAction = listener.onClickCancelGooglePermissionDialog)\n\n  def showRequestPermissionsDialog(): TaskService[Unit] =\n    showErrorDialog(\n      message = R.string.errorFineLocationMessage,\n      action = listener.onClickOkPermissionsDialog,\n      negativeAction = listener.onClickCancelPermissionsDialog)\n\n  def showSelectAccountDialog(): TaskService[Unit] =\n    showErrorDialog(\n      message = R.string.errorAccountsMessage,\n      action = listener.onClickOkSelectAccountsDialog,\n      negativeAction = listener.onClickCancelSelectAccountsDialog)\n\n  private[this] def showErrorDialog(\n      message: Int,\n      action: () => Unit,\n      negativeAction: () => Unit): TaskService[Unit] =\n    TaskService[Unit] {\n      CatchAll[UiException] {\n        context.original.get match {\n          case Some(activity: AppCompatActivity) =>\n            val fm = activity.getSupportFragmentManager\n            val ft = fm.beginTransaction()\n            Option(fm.findFragmentByTag(tagDialog)) foreach ft.remove\n            val dialog = new AlertDialogFragment(\n              message = message,\n              positiveAction = action,\n              negativeAction = negativeAction)\n            ft.add(dialog, tagDialog).addToBackStack(javaNull)\n            ft.commitAllowingStateLoss()\n          case _ =>\n        }\n      }\n    }\n\n  // Devices Layout\n\n  def userRadio(title: String, tag: String, visible: Boolean = true): RadioButton =\n    (w[RadioButton] <~\n      radioStyle <~\n      tvText(title) <~\n      vTag(tag) <~\n      (if (visible) vVisible else vGone)).get\n\n  def userRadioSubtitle(text: String, visible: Boolean = true): TextView =\n    (w[TextView] <~\n      radioSubtitleStyle <~\n      tvText(text) <~\n      (if (visible) vVisible else vGone)).get\n\n  def otherDevicesLink(text: String): TextView =\n    (w[TextView] <~\n      otherDevicesLinkStyle <~\n      tvUnderlineText(text) <~\n      FuncOn.click { v: View =>\n        (dom.devicesGroup <~ Transformer {\n          case view if view.getVisibility == View.GONE => view <~ vVisible\n          case _                                       => Ui.nop\n        }) ~ (v <~ vGone)\n      }).get\n\n  def addV1DevicesToRadioGroup(devices: UserCloudDevices): Ui[Any] = {\n\n    val subtitle: String = resGetString(R.string.deviceMigratedFromV1)\n\n    val userRadioView = devices.dataV1.lastOption.toSeq flatMap { device =>\n      Seq(\n        userRadio(resGetString(R.string.currentDeviceTitle, device.deviceName), device.deviceId),\n        userRadioSubtitle(subtitle))\n    }\n\n    val newConfRadioView = Seq(\n      userRadio(\n        resGetString(R.string.loadUserConfigDeviceReplace, Build.MODEL),\n        newConfigurationKey),\n      userRadioSubtitle(resGetString(R.string.newConfigurationSubtitle)))\n\n    val allRadioViews = {\n\n      val radioViews = devices.dataV1 flatMap { device =>\n        Seq(\n          userRadio(device.deviceName, device.deviceId, visible = false),\n          userRadioSubtitle(subtitle, visible = false))\n      }\n\n      if (radioViews.isEmpty) radioViews\n      else {\n        otherDevicesLink(resGetString(R.string.otherDevicesLink)) +: radioViews\n      }\n    }\n\n    val radioViews = userRadioView ++ newConfRadioView ++ allRadioViews\n\n    (dom.devicesGroup <~ vgRemoveAllViews <~ vgAddViews(radioViews)) ~\n      Ui {\n        radioViews.headOption match {\n          case Some(radioButton: RadioButton) => radioButton.setChecked(true)\n          case _                              =>\n        }\n      }\n  }\n\n  def addGoogleDriveDevicesToRadioGroup(devices: UserCloudDevices): Ui[Any] = {\n\n    def subtitle(device: UserCloudDevice): String = {\n      val time = new PrettyTime().format(device.modifiedDate)\n      resGetString(R.string.syncLastSynced, time)\n    }\n\n    val userRadioView = devices.userDevice.toSeq.flatMap { device =>\n      Seq(\n        userRadio(resGetString(R.string.currentDeviceTitle, device.deviceName), device.cloudId),\n        userRadioSubtitle(subtitle(device)))\n    }\n\n    val newConfRadioView = Seq(\n      userRadio(\n        resGetString(R.string.loadUserConfigDeviceReplace, Build.MODEL),\n        newConfigurationKey),\n      userRadioSubtitle(resGetString(R.string.newConfigurationSubtitle)))\n\n    val allRadioViews = {\n\n      val radioViews = devices.devices flatMap { device =>\n        Seq(\n          userRadio(device.deviceName, device.cloudId, visible = false),\n          userRadioSubtitle(subtitle(device), visible = false))\n      }\n\n      if (radioViews.isEmpty) radioViews\n      else {\n        otherDevicesLink(resGetString(R.string.otherDevicesLink)) +: radioViews\n      }\n    }\n\n    val radioViews = userRadioView ++ newConfRadioView ++ allRadioViews\n\n    (dom.devicesGroup <~ vgRemoveAllViews <~ vgAddViews(radioViews)) ~\n      Ui {\n        radioViews.headOption match {\n          case Some(radioButton: RadioButton) => radioButton.setChecked(true)\n          case _                              =>\n        }\n      }\n  }\n\n  // Styles\n\n  private[this] def defaultActionStyle(implicit context: ActivityContextWrapper): Tweak[Button] =\n    vBackgroundTint(resGetColor(R.color.primary))\n\n  private[this] def diveInActionStyle(implicit context: ActivityContextWrapper): Tweak[Button] =\n    vInvisible +\n      vBackgroundTint(resGetColor(R.color.wizard_background_action_disable)) +\n      vEnabled(false)\n\n  private[this] def radioStyle(implicit context: ActivityContextWrapper): Tweak[RadioButton] = {\n    val padding = resGetDimensionPixelSize(R.dimen.padding_checkbox)\n    vWrapContent +\n      vPadding(paddingLeft = padding, paddingRight = padding) +\n      tvGravity(Gravity.CENTER_VERTICAL)\n\n  }\n\n  private[this] def radioSubtitleStyle(implicit context: ActivityContextWrapper): Tweak[TextView] =\n    vWrapContent +\n      vPadding(\n        paddingLeft = resGetDimensionPixelSize(R.dimen.margin_left_subtitle),\n        paddingRight = resGetDimensionPixelSize(R.dimen.padding_default),\n        paddingBottom = resGetDimensionPixelSize(R.dimen.padding_default))\n\n  private[this] def otherDevicesLinkStyle(\n      implicit context: ActivityContextWrapper): Tweak[TextView] =\n    vMatchWidth +\n      tvGravity(Gravity.CENTER_HORIZONTAL) +\n      vPaddings(resGetDimensionPixelSize(R.dimen.padding_large)) +\n      tvColorResource(R.color.primary)\n\n  private[this] def paginationItemStyle(implicit context: ContextWrapper): Tweak[ImageView] = {\n    val size   = resGetDimensionPixelSize(R.dimen.wizard_size_pager)\n    val margin = resGetDimensionPixelSize(R.dimen.wizard_margin_pager)\n    lp[ViewGroup](size, size) +\n      llLayoutMargin(margin, margin, margin, margin)\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/app/ui/wizard/models/Models.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.app.ui.wizard.models\n\nimport java.util.Date\n\nimport cards.nine.models.UserV1Device\n\ncase class UserCloudDevices(\n    deviceType: DeviceType,\n    name: String,\n    userDevice: Option[UserCloudDevice],\n    devices: Seq[UserCloudDevice],\n    dataV1: Seq[UserV1Device])\n\ncase class UserCloudDevice(\n    deviceName: String,\n    cloudId: String,\n    currentDevice: Boolean,\n    modifiedDate: Date)\n\nsealed trait DeviceType\n\ncase object NoFoundDeviceType extends DeviceType\n\ncase object V1DeviceType extends DeviceType\n\ncase object GoogleDriveDeviceType extends DeviceType\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/process/cloud/CloudStorageProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.cloud\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models._\nimport com.google.android.gms.common.ConnectionResult\nimport com.google.android.gms.common.api.GoogleApiClient\n\ntrait CloudStorageProcess {\n\n  /**\n   * Creates the cloud storage API client\n   * The ContextSupport should have an original Context of type CloudStorageClientListener\n   * @param account the email for the client\n   * @return the GoogleAPIClient\n   */\n  def createCloudStorageClient(account: String)(\n      implicit contextSupport: ContextSupport): TaskService[GoogleApiClient]\n\n  /**\n   * Transform a sequence of devices into a tuple containing a possible configuration for the actual device and a list\n   * with the remaining elements\n   * @param client the google API client\n   * @param devices sequence of devices\n   * @return tuple of an optional CloudStorageDeviceSummary and a list of CloudStorageDeviceSummary\n   * @throws CloudStorageProcessException if the service throws an error\n   */\n  def prepareForActualDevice[T <: CloudStorageResource](client: GoogleApiClient, devices: Seq[T])(\n      implicit context: ContextSupport): TaskService[(Option[T], Seq[T])]\n\n  /**\n   * Return a sequence of `CloudStorageResource` filtered by device type\n   * @param client the google API client\n   * @return sequence of `CloudStorageResource`\n   * @throws CloudStorageProcessException if the service throws an error\n   */\n  def getCloudStorageDevices(client: GoogleApiClient)(\n      implicit context: ContextSupport): TaskService[Seq[CloudStorageDeviceSummary]]\n\n  /**\n   * Fetch a `CloudStorageDevice` by his id\n   * @param cloudStorageResourceId google drive identifier\n   * @return the `CloudStorageDevice`\n   * @throws CloudStorageProcessException if the device not exists or the service throws an error\n   */\n  def getCloudStorageDevice(\n      client: GoogleApiClient,\n      cloudStorageResourceId: String): TaskService[CloudStorageDevice]\n\n  /**\n   * Fetch the raw content of a device by his id\n   * @param client the google API client\n   * @param cloudStorageResourceId google drive identifier\n   * @return the `RawCloudStorageDevice`\n   * @throws CloudStorageProcessException if the device not exists or the service throws an error\n   */\n  def getRawCloudStorageDevice(\n      client: GoogleApiClient,\n      cloudStorageResourceId: String): TaskService[RawCloudStorageDevice]\n\n  /**\n   * Create or update a device in the cloud\n   * @param client the google API client\n   * @param maybeCloudId None if a new device should be created. Some(id) for updating an existing device\n   * @param cloudStorageDevice the device to create or update\n   * @return the saved device\n   * @throws CloudStorageProcessException if the services throws an error\n   */\n  def createOrUpdateCloudStorageDevice(\n      client: GoogleApiClient,\n      maybeCloudId: Option[String],\n      cloudStorageDevice: CloudStorageDeviceData): TaskService[CloudStorageDevice]\n\n  /**\n   * Create or update a device the collections, moments and dockApps using as actual devices\n   * @param client the google API client\n   * @param collections the collections to be overwritten in the actual devices\n   * @param moments the moments to be overwritten in the actual devices\n   * @param dockApps the dockApps to be overwritten in the actual devices\n   * @return the saved device\n   * @throws CloudStorageProcessException if the services throws an error\n   */\n  def createOrUpdateActualCloudStorageDevice(\n      client: GoogleApiClient,\n      collections: Seq[CloudStorageCollection],\n      moments: Seq[CloudStorageMoment],\n      dockApps: Seq[CloudStorageDockApp])(\n      implicit context: ContextSupport): TaskService[CloudStorageDevice]\n\n  /**\n   * Delete a `CloudStorageDevice` by his id\n   * @param client the google API client\n   * @param cloudId identifier of the device\n   * @return Unit\n   * @throws CloudStorageProcessException if the device not exists or the service throws an error\n   */\n  def deleteCloudStorageDevice(client: GoogleApiClient, cloudId: String): TaskService[Unit]\n\n}\n\nobject CloudStorageProcess {\n\n  val actualDocumentVersion = 1\n\n}\n\ntrait CloudStorageClientListener {\n\n  def onDriveConnectionSuspended(cause: Int): Unit\n\n  def onDriveConnected(): Unit\n\n  def onDriveConnectionFailed(connectionResult: ConnectionResult): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/process/cloud/Conversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.cloud\n\nimport cards.nine.app.ui.wizard.models.UserCloudDevice\nimport cards.nine.models.NineCardsIntentImplicits._\nimport cards.nine.models._\nimport cards.nine.models.{UserV1Collection, UserV1CollectionItem, UserV1Device}\nimport cards.nine.services.drive.models.DriveServiceFileSummary\nimport play.api.libs.json.Json\n\nobject Conversions extends NineCardsIntentConversions {\n\n  def toCloudStorageDeviceSummary(\n      driveServiceFile: DriveServiceFileSummary,\n      maybeCloudId: Option[String]): CloudStorageDeviceSummary =\n    CloudStorageDeviceSummary(\n      cloudId = driveServiceFile.uuid,\n      deviceName = driveServiceFile.title,\n      deviceId = driveServiceFile.deviceId,\n      createdDate = driveServiceFile.createdDate,\n      modifiedDate = driveServiceFile.modifiedDate,\n      currentDevice = maybeCloudId contains driveServiceFile.uuid)\n\n  def toCloudStorageDevice(userDevice: UserV1Device): CloudStorageDeviceData =\n    CloudStorageDeviceData(\n      deviceId = userDevice.deviceId,\n      deviceName = userDevice.deviceName,\n      documentVersion = CloudStorageProcess.actualDocumentVersion,\n      userDevice.collections map toCloudStorageCollection,\n      moments = None,\n      dockApps = None)\n\n  def toCloudStorageCollection(userCollection: UserV1Collection): CloudStorageCollection =\n    CloudStorageCollection(\n      name = userCollection.name,\n      originalSharedCollectionId = userCollection.originalSharedCollectionId,\n      sharedCollectionId = userCollection.sharedCollectionId,\n      sharedCollectionSubscribed = userCollection.sharedCollectionSubscribed,\n      items = userCollection.items map toCloudStorageCollectionItem,\n      collectionType = userCollection.collectionType,\n      icon = userCollection.icon,\n      category = userCollection.category,\n      moment = None)\n\n  def toCloudStorageCollectionItem(\n      userCollectionItem: UserV1CollectionItem): CloudStorageCollectionItem =\n    CloudStorageCollectionItem(\n      itemType = userCollectionItem.itemType.name,\n      title = userCollectionItem.title,\n      intent = userCollectionItem.intent)\n\n  def toCloudStorageCollection(\n      collection: Collection,\n      widgets: Option[Seq[Widget]]): CloudStorageCollection =\n    CloudStorageCollection(\n      name = collection.name,\n      originalSharedCollectionId = collection.originalSharedCollectionId,\n      sharedCollectionId = collection.sharedCollectionId,\n      sharedCollectionSubscribed = Some(collection.sharedCollectionSubscribed),\n      items = collection.cards map toCloudStorageCollectionItem,\n      collectionType = collection.collectionType,\n      icon = collection.icon,\n      category = collection.appsCategory,\n      moment = collection.moment map (moment => toCloudStorageMoment(moment, widgets)))\n\n  def toCloudStorageCollectionItem(card: Card): CloudStorageCollectionItem =\n    CloudStorageCollectionItem(\n      itemType = card.cardType.name,\n      title = card.term,\n      intent = Json.toJson(card.intent).toString())\n\n  def toCloudStorageMoment(moment: Moment, widgets: Option[Seq[Widget]]): CloudStorageMoment =\n    CloudStorageMoment(\n      timeslot = moment.timeslot map toCloudStorageMomentTimeSlot,\n      wifi = moment.wifi,\n      bluetooth = Option(moment.bluetooth),\n      headphones = moment.headphone,\n      momentType = moment.momentType,\n      widgets = widgets map toCloudStorageWidgetSeq)\n\n  def toCloudStorageWidgetSeq(widgets: Seq[Widget]): Seq[CloudStorageWidget] =\n    widgets map toCloudStorageWidget\n\n  def toCloudStorageWidget(widget: Widget): CloudStorageWidget =\n    CloudStorageWidget(\n      packageName = widget.packageName,\n      className = widget.className,\n      area = toCloudStorageWidgetArea(widget.area),\n      widgetType = widget.widgetType,\n      label = widget.label,\n      imagePath = widget.imagePath,\n      intent = widget.intent map nineCardIntentToJson)\n\n  def toCloudStorageWidgetArea(area: WidgetArea): CloudStorageWidgetArea =\n    CloudStorageWidgetArea(\n      startX = area.startX,\n      startY = area.startY,\n      spanX = area.spanX,\n      spanY = area.spanY)\n\n  def toCloudStorageMomentTimeSlot(timeSlot: MomentTimeSlot): CloudStorageMomentTimeSlot =\n    CloudStorageMomentTimeSlot(from = timeSlot.from, to = timeSlot.to, days = timeSlot.days)\n\n  def toCloudStorageDockApp(dockApp: DockApp): CloudStorageDockApp =\n    CloudStorageDockApp(\n      name = dockApp.name,\n      dockType = dockApp.dockType,\n      intent = Json.toJson(dockApp.intent).toString(),\n      imagePath = dockApp.imagePath,\n      position = dockApp.position)\n\n  def toUserCloudDevice(device: CloudStorageDeviceSummary): UserCloudDevice =\n    UserCloudDevice(\n      deviceName = device.deviceName,\n      cloudId = device.cloudId,\n      currentDevice = device.currentDevice,\n      modifiedDate = device.modifiedDate)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/process/cloud/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.cloud\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\nsealed trait CloudStorageError\n\ncase object SigInRequired extends CloudStorageError\n\ncase object RateLimitExceeded extends CloudStorageError\n\ncase object ResourceNotAvailable extends CloudStorageError\n\ncase class CloudStorageProcessException(\n    message: String,\n    cause: Option[Throwable] = None,\n    driveError: Option[CloudStorageError] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/process/cloud/impl/CloudStorageProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.cloud.impl\n\nimport java.util.Date\n\nimport android.os.{Build, Bundle}\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{Conversions => _, _}\nimport cards.nine.process.cloud._\nimport cards.nine.process.cloud.models.CloudStorageImplicits._\nimport cards.nine.services.drive.models.DriveServiceFileSummary\nimport cards.nine.services.drive.{Conversions => _, _}\nimport cards.nine.services.persistence.PersistenceServices\nimport cats.syntax.either._\nimport com.google.android.gms.common.ConnectionResult\nimport com.google.android.gms.common.api.GoogleApiClient\nimport monix.eval.Task\nimport play.api.libs.json.Json\n\nimport scala.util.{Failure, Success, Try}\n\nclass CloudStorageProcessImpl(\n    driveServices: DriveServices,\n    persistenceServices: PersistenceServices)\n    extends CloudStorageProcess {\n\n  import Conversions._\n\n  private[this] val userDeviceType = \"USER_DEVICE\"\n\n  private[this] val jsonMimeType = \"application/json\"\n\n  private[this] val noActiveUserErrorMessage = \"No active user\"\n\n  private[this] val userNotFoundErrorMessage = (id: Int) =>\n    s\"User with id $id not found in the database\"\n\n  override def createCloudStorageClient(account: String)(implicit contextSupport: ContextSupport) =\n    driveServices\n      .createDriveClient(account)\n      .resolveSides(mapRight = googleApiClient => {\n        contextSupport.getOriginal.get match {\n          case Some(listener: CloudStorageClientListener) =>\n            googleApiClient.registerConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks {\n              override def onConnectionSuspended(cause: Int): Unit =\n                listener.onDriveConnectionSuspended(cause)\n\n              override def onConnected(bundle: Bundle): Unit =\n                listener.onDriveConnected()\n            })\n            googleApiClient.registerConnectionFailedListener(\n              new GoogleApiClient.OnConnectionFailedListener {\n                override def onConnectionFailed(connectionResult: ConnectionResult): Unit =\n                  listener.onDriveConnectionFailed(connectionResult)\n              })\n            Right(googleApiClient)\n          case Some(_) =>\n            Left(\n              CloudStorageProcessException(\n                \"The implicit activity is not a CloudStorageClientListener\"))\n          case None =>\n            Left(CloudStorageProcessException(\"The implicit activity is null\"))\n        }\n      }, mapLeft = (e) => Left(CloudStorageProcessException(e.getMessage, Option(e))))\n\n  override def prepareForActualDevice[T <: CloudStorageResource](\n      client: GoogleApiClient,\n      devices: Seq[T])(implicit context: ContextSupport) = {\n\n    def sort(devices: Seq[T]): Seq[T] =\n      devices.sortBy(_.modifiedDate)(Ordering[Date].reverse)\n\n    def fixAndSort(androidId: String, devices: Seq[T]): (Option[T], Seq[T]) = {\n\n      val (userDevices, otherDevices) =\n        devices.partition(_.deviceId.contains(androidId))\n\n      val sortedUserDevices = sort(userDevices)\n\n      val fixedUserDevice = sortedUserDevices.drop(1)\n\n      (sortedUserDevices.headOption, sort(fixedUserDevice ++ otherDevices))\n    }\n\n    (for {\n      androidId <- persistenceServices.getAndroidId\n      fixedDevices = fixAndSort(androidId, devices)\n    } yield fixedDevices).leftMap(mapException)\n  }\n\n  override def getCloudStorageDevices(client: GoogleApiClient)(\n      implicit context: ContextSupport): TaskService[Seq[CloudStorageDeviceSummary]] =\n    context.getActiveUserId map { id =>\n      (for {\n        driveServicesSeq <- driveServices.listFiles(client, Option(userDeviceType))\n        maybeCloudId     <- findUserDeviceCloudId(id)\n      } yield driveServicesSeq map (file => toCloudStorageDeviceSummary(file, maybeCloudId)))\n        .leftMap(mapException)\n    } getOrElse {\n      TaskService(Task(Either.left(CloudStorageProcessException(noActiveUserErrorMessage))))\n    }\n\n  override def getCloudStorageDevice(client: GoogleApiClient, cloudId: String) = {\n\n    def parseDevice(json: String): TaskService[CloudStorageDeviceData] =\n      TaskService {\n        Task {\n          Try(Json.parse(json).as[CloudStorageDeviceData]) match {\n            case Success(s) => Right(s)\n            case Failure(e) =>\n              Left(CloudStorageProcessException(message = e.getMessage, cause = Option(e)))\n          }\n        }\n      }\n\n    def fixMomentsInCollection(collection: CloudStorageCollection): CloudStorageCollection =\n      collection.copy(moment = collection.moment.toSeq.headOption)\n\n    def fixMoments(device: CloudStorageDeviceData): CloudStorageDeviceData =\n      device.copy(\n        moments = device.moments,\n        collections = device.collections map fixMomentsInCollection)\n\n    (for {\n      driveFile   <- driveServices.readFile(client, cloudId)\n      device      <- parseDevice(driveFile.content)\n      fixedDevice <- TaskService.right(fixMoments(device))\n    } yield\n      CloudStorageDevice(\n        cloudId = cloudId,\n        createdDate = driveFile.summary.createdDate,\n        modifiedDate = driveFile.summary.modifiedDate,\n        data = fixedDevice)).leftMap(mapException)\n  }\n\n  override def getRawCloudStorageDevice(client: GoogleApiClient, cloudId: String) =\n    driveServices\n      .readFile(client, cloudId)\n      .map { driveFile =>\n        RawCloudStorageDevice(\n          cloudId = cloudId,\n          uuid = driveFile.summary.uuid,\n          deviceId = driveFile.summary.deviceId,\n          title = driveFile.summary.title,\n          createdDate = driveFile.summary.createdDate,\n          modifiedDate = driveFile.summary.modifiedDate,\n          json = driveFile.content)\n      }\n      .leftMap(mapException)\n\n  override def createOrUpdateCloudStorageDevice(\n      client: GoogleApiClient,\n      maybeCloudId: Option[String],\n      cloudStorageDeviceData: CloudStorageDeviceData) =\n    createOrUpdate(client, maybeCloudId, cloudStorageDeviceData)\n\n  override def createOrUpdateActualCloudStorageDevice(\n      client: GoogleApiClient,\n      collections: Seq[CloudStorageCollection],\n      moments: Seq[CloudStorageMoment],\n      dockApps: Seq[CloudStorageDockApp])(implicit context: ContextSupport) = {\n\n    def deviceExists(maybeCloudId: Option[String]): TaskService[Option[String]] =\n      maybeCloudId match {\n        case Some(cloudId) =>\n          driveServices.fileExists(client, cloudId)\n        case _ =>\n          TaskService(Task(Either.right(None)))\n      }\n\n    context.getActiveUserId map { id =>\n      (for {\n        androidId    <- persistenceServices.getAndroidId\n        maybeCloudId <- findUserDeviceCloudId(id)\n        maybeName    <- deviceExists(maybeCloudId)\n        cloudStorageDeviceData = CloudStorageDeviceData(\n          deviceId = androidId,\n          deviceName = maybeName getOrElse Build.MODEL,\n          documentVersion = CloudStorageProcess.actualDocumentVersion,\n          collections = collections,\n          moments = Some(moments),\n          dockApps = Some(dockApps))\n        device <- createOrUpdate(\n          client = client,\n          maybeCloudId = if (maybeName.nonEmpty) maybeCloudId else None,\n          cloudStorageDeviceData = cloudStorageDeviceData)\n      } yield device).leftMap(mapException)\n    } getOrElse {\n      TaskService(Task(Either.left(CloudStorageProcessException(noActiveUserErrorMessage))))\n    }\n  }\n\n  override def deleteCloudStorageDevice(client: GoogleApiClient, cloudId: String) =\n    driveServices.deleteFile(client, cloudId).leftMap(mapException)\n\n  private[this] def createOrUpdate(\n      client: GoogleApiClient,\n      maybeCloudId: Option[String],\n      cloudStorageDeviceData: CloudStorageDeviceData): TaskService[CloudStorageDevice] = {\n\n    def createOrUpdateFile(\n        maybeCloudId: Option[String],\n        title: String,\n        content: String,\n        deviceId: String): TaskService[DriveServiceFileSummary] =\n      maybeCloudId match {\n        case Some(cloudId) =>\n          driveServices.updateFile(client, cloudId, title, content)\n        case _ =>\n          driveServices.createFile(client, title, content, deviceId, userDeviceType, jsonMimeType)\n      }\n\n    def deviceToJson(device: CloudStorageDeviceData): TaskService[String] =\n      TaskService {\n        Task {\n          Try(Json.toJson(device).toString()) match {\n            case Success(s) => Either.right(s)\n            case Failure(e) =>\n              Either.left(CloudStorageProcessException(message = e.getMessage, cause = Some(e)))\n          }\n        }\n      }\n\n    (for {\n      json <- deviceToJson(cloudStorageDeviceData)\n      summary <- createOrUpdateFile(\n        maybeCloudId,\n        cloudStorageDeviceData.deviceName,\n        json,\n        cloudStorageDeviceData.deviceId)\n    } yield {\n      CloudStorageDevice(\n        cloudId = summary.uuid,\n        createdDate = summary.createdDate,\n        modifiedDate = summary.modifiedDate,\n        data = cloudStorageDeviceData)\n    }).leftMap(mapException)\n  }\n\n  private[this] def findUserDeviceCloudId(userId: Int): TaskService[Option[String]] = TaskService {\n    persistenceServices.findUserById(userId).value map {\n      case Right(Some(user)) => Right(user.deviceCloudId)\n      case Right(None) =>\n        Left(CloudStorageProcessException(userNotFoundErrorMessage(userId)))\n      case Left(e) =>\n        Either.left(CloudStorageProcessException(e.getMessage, Some(e)))\n    }\n  }\n\n  private[this] def mapException: (Throwable) => NineCardException = {\n    case e: DriveServicesException =>\n      CloudStorageProcessException(\n        message = e.message,\n        cause = Option(e),\n        driveError = e.googleDriveError flatMap driveErrorToCloudStorageError)\n    case e: CloudStorageProcessException => e\n    case t                               => CloudStorageProcessException(t.getMessage, Option(t))\n  }\n\n  private[this] def driveErrorToCloudStorageError(\n      driveError: GoogleDriveError): Option[CloudStorageError] =\n    driveError match {\n      case DriveSigInRequired        => Option(SigInRequired)\n      case DriveRateLimitExceeded    => Option(RateLimitExceeded)\n      case DriveResourceNotAvailable => Option(ResourceNotAvailable)\n      case _                         => None\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/process/cloud/models/CloudStorageImplicits.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.cloud.models\n\nimport cards.nine.models._\nimport cards.nine.models.types.json._\n\nobject CloudStorageImplicits {\n\n  import play.api.libs.json._\n  import NineCardCategoryImplicits._\n  import NineCardsMomentImplicits._\n  import CollectionTypeImplicits._\n  import DockTypeImplicits._\n  import WidgetTypeImplicits._\n\n  implicit val cloudStorageWidgetAreaReads = Json.reads[CloudStorageWidgetArea]\n  implicit val cloudStorageWidgetReads     = Json.reads[CloudStorageWidget]\n  implicit val cloudStorageDockAppReads    = Json.reads[CloudStorageDockApp]\n  implicit val cloudStorageMomentTimeSlotReads =\n    Json.reads[CloudStorageMomentTimeSlot]\n  implicit val cloudStorageMomentReads = Json.reads[CloudStorageMoment]\n  implicit val cloudStorageCollectionItemReads =\n    Json.reads[CloudStorageCollectionItem]\n  implicit val cloudStorageCollectionReads = Json.reads[CloudStorageCollection]\n  implicit val cloudStorageDeviceReads     = Json.reads[CloudStorageDeviceData]\n\n  implicit val cloudStorageWidgetAreaWrites =\n    Json.writes[CloudStorageWidgetArea]\n  implicit val cloudStorageWidgetWrites  = Json.writes[CloudStorageWidget]\n  implicit val cloudStorageDockAppWrites = Json.writes[CloudStorageDockApp]\n  implicit val cloudStorageMomentTimeSlotWrites =\n    Json.writes[CloudStorageMomentTimeSlot]\n  implicit val cloudStorageMomentWrites = Json.writes[CloudStorageMoment]\n  implicit val cloudStorageCollectionItemWrites =\n    Json.writes[CloudStorageCollectionItem]\n  implicit val cloudStorageCollectionWrites =\n    Json.writes[CloudStorageCollection]\n  implicit val cloudStorageDeviceWrites = Json.writes[CloudStorageDeviceData]\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/process/commons/Models.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.commons\n\nimport com.google.android.gms.common.api.GoogleApiClient\n\nsealed trait ConnectionSuspendedCause\n\ncase object CauseNetworkLost extends ConnectionSuspendedCause\n\ncase object CauseServiceDisconnected extends ConnectionSuspendedCause\n\ncase object CauseUnknown extends ConnectionSuspendedCause\n\nobject ConnectionSuspendedCause {\n\n  def apply(cause: Int): ConnectionSuspendedCause =\n    cause match {\n      case GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST =>\n        CauseNetworkLost\n      case GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED =>\n        CauseServiceDisconnected\n      case _ => CauseUnknown\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/process/social/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.social\n\nimport cards.nine.commons.services.TaskService.NineCardException\nimport cards.nine.process.commons.ConnectionSuspendedCause\nimport com.google.android.gms.common.ConnectionResult\n\ncase class SocialProfileProcessException(\n    message: String,\n    cause: Option[Throwable] = None,\n    recoverable: Boolean = false)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ncase class SocialProfileConnectionSuspendedServicesException(\n    message: String,\n    googleCauseCode: ConnectionSuspendedCause,\n    cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ncase class SocialProfileConnectionFailedServicesException(\n    message: String,\n    connectionResult: Option[ConnectionResult],\n    cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/process/social/SocialProfileProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.social\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport com.google.android.gms.common.ConnectionResult\nimport com.google.android.gms.common.api.GoogleApiClient\n\ntrait SocialProfileProcess {\n\n  /**\n   * Creates the social profile API client\n   * The ActivityContextSupport should have an activity of type SocialProfileClientListener\n   * @param clientId the OAuth Client Id for requesting the token Id\n   * @param account the email for the client\n   * @return the GoogleAPIClient\n   */\n  def createSocialProfileClient(clientId: String, account: String)(\n      implicit contextSupport: ContextSupport): TaskService[GoogleApiClient]\n\n  /**\n   * Load the user information for Google Plus and updates the values on the database\n   * @param client the google API client\n   * @return the profile name\n   */\n  def updateUserProfile(client: GoogleApiClient)(\n      implicit context: ContextSupport): TaskService[Option[String]]\n\n}\n\ntrait SocialProfileClientListener {\n\n  def onPlusConnectionSuspended(cause: Int): Unit\n\n  def onPlusConnected(): Unit\n\n  def onPlusConnectionFailed(connectionResult: ConnectionResult): Unit\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/process/social/impl/SocialProfileProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.social.impl\n\nimport android.os.Bundle\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{User, UserProfile}\nimport cards.nine.process.social._\nimport cards.nine.services.persistence.{PersistenceServiceException, PersistenceServices}\nimport cards.nine.services.plus.models.GooglePlusProfile\nimport cards.nine.services.plus.{GooglePlusServices, GooglePlusServicesException}\nimport cats.syntax.either._\nimport com.google.android.gms.common.ConnectionResult\nimport com.google.android.gms.common.api.GoogleApiClient\nimport monix.eval.Task\n\nclass SocialProfileProcessImpl(\n    googlePlusServices: GooglePlusServices,\n    persistenceServices: PersistenceServices)\n    extends SocialProfileProcess {\n\n  val me = \"me\"\n\n  private[this] val noActiveUserErrorMessage = \"No active user\"\n\n  override def createSocialProfileClient(clientId: String, account: String)(\n      implicit contextSupport: ContextSupport) =\n    googlePlusServices\n      .createGooglePlusClient(clientId, account)\n      .resolveSides(mapRight = googleAplClient => {\n        contextSupport.getOriginal.get match {\n          case Some(listener: SocialProfileClientListener) =>\n            googleAplClient.registerConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks {\n              override def onConnectionSuspended(cause: Int): Unit =\n                listener.onPlusConnectionSuspended(cause)\n\n              override def onConnected(bundle: Bundle): Unit =\n                listener.onPlusConnected()\n            })\n            googleAplClient.registerConnectionFailedListener(\n              new GoogleApiClient.OnConnectionFailedListener {\n                override def onConnectionFailed(connectionResult: ConnectionResult): Unit =\n                  listener.onPlusConnectionFailed(connectionResult)\n              })\n            Right(googleAplClient)\n          case Some(_) =>\n            Left(\n              SocialProfileProcessException(\n                \"The implicit activity is not a SocialProfileClientListener\"))\n          case None =>\n            Left(SocialProfileProcessException(\"The implicit activity is null\"))\n        }\n      }, mapLeft = (e) => Left(SocialProfileProcessException(e.getMessage, Option(e))))\n\n  override def updateUserProfile(client: GoogleApiClient)(implicit context: ContextSupport) = {\n\n    def updateUser(maybeUser: Option[User], googlePlusProfile: GooglePlusProfile) = {\n\n      def toUser(user: User, googlePlusProfile: GooglePlusProfile) =\n        user.copy(\n          userProfile = UserProfile(\n            name = googlePlusProfile.name,\n            avatar = googlePlusProfile.avatarUrl,\n            cover = googlePlusProfile.coverUrl))\n\n      maybeUser match {\n        case Some(user) =>\n          persistenceServices.updateUser(toUser(user, googlePlusProfile))\n        case None =>\n          TaskService(Task(Either.left(PersistenceServiceException(noActiveUserErrorMessage))))\n      }\n\n    }\n\n    def findAndUpdateUserProfile(googlePlusProfile: GooglePlusProfile)(\n        implicit context: ContextSupport) =\n      context.getActiveUserId map { userId =>\n        (for {\n          maybeUser <- persistenceServices.findUserById(userId)\n          _         <- updateUser(maybeUser, googlePlusProfile)\n        } yield ()).leftMap(onException)\n      } getOrElse {\n        TaskService(Task(Either.left(SocialProfileProcessException(noActiveUserErrorMessage))))\n      }\n\n    (for {\n      googlePlusProfile <- googlePlusServices.loadUserProfile(client)\n      _                 <- findAndUpdateUserProfile(googlePlusProfile)\n    } yield googlePlusProfile.name).leftMap {\n      case gPlusException: GooglePlusServicesException =>\n        SocialProfileProcessException(\n          gPlusException.getMessage,\n          Option(gPlusException),\n          gPlusException.recoverable)\n      case t => SocialProfileProcessException(t.getMessage, Option(t))\n    }\n  }\n\n  private[this] def onException: (Throwable) => NineCardException = {\n    case e: GooglePlusServicesException =>\n      SocialProfileProcessException(e.getMessage, Option(e), e.recoverable)\n    case e => SocialProfileProcessException(e.getMessage, Option(e))\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/process/thirdparty/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.thirdparty\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class ExternalServicesProcessException(\n    message: String,\n    cause: Option[Throwable] = None,\n    recoverable: Boolean = false)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsExternalServicesProcessException {\n  implicit def externalServicesProcessException =\n    (t: Throwable) => ExternalServicesProcessException(t.getMessage, Option(t))\n}\n\ncase class TokenFirebaseException(\n    message: String,\n    cause: Option[Throwable] = None,\n    recoverable: Boolean = false)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsTokenFirebaseException {\n  implicit def tokenFirebaseException =\n    (t: Throwable) => TokenFirebaseException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/process/thirdparty/ExternalServicesProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.thirdparty\n\nimport android.os.StrictMode\nimport cards.nine.app.ui.commons.AppLog\nimport cards.nine.app.ui.commons.ops.UiOps._\nimport cards.nine.app.ui.preferences.commons.IsStethoActive\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport com.apptentive.android.sdk.Apptentive\nimport com.crashlytics.android.Crashlytics\nimport com.crashlytics.android.core.CrashlyticsCore\nimport com.facebook.stetho.Stetho\nimport com.fortysevendeg.ninecardslauncher.R\nimport com.google.firebase.analytics.FirebaseAnalytics\nimport com.google.firebase.iid.FirebaseInstanceId\nimport io.fabric.sdk.android.Fabric\nimport io.flowup.FlowUp\nimport macroid.Ui\n\nclass ExternalServicesProcess\n    extends ImplicitsExternalServicesProcessException\n    with ImplicitsTokenFirebaseException {\n\n  def initializeCrashlytics(implicit contextSupport: ContextSupport): TaskService[Unit] =\n    TaskService {\n      CatchAll[ExternalServicesProcessException] {\n        if (readFlag(R.string.crashlytics_enabled) && getString(R.string.crashlytics_api_key).nonEmpty) {\n          AppLog.info(\"Initializing Crashlytics\")\n          Fabric.`with`(\n            contextSupport.context,\n            new Crashlytics.Builder().core(new CrashlyticsCore.Builder().build()).build())\n        }\n      }\n    }\n\n  def initializeStrictMode(implicit contextSupport: ContextSupport): TaskService[Unit] =\n    TaskService {\n      CatchAll[ExternalServicesProcessException] {\n        if (readFlag(R.string.strict_mode_enabled)) {\n          AppLog.info(\"Initializing Strict Mode\")\n          StrictMode.setThreadPolicy(\n            new StrictMode.ThreadPolicy.Builder()\n              .detectDiskReads()\n              .detectDiskWrites()\n              .detectAll()\n              .penaltyLog()\n              .build())\n          StrictMode.setVmPolicy(\n            new StrictMode.VmPolicy.Builder()\n              .detectLeakedSqlLiteObjects()\n              .detectLeakedClosableObjects()\n              .detectAll()\n              .penaltyLog()\n              .build())\n        }\n      }\n    }\n\n  def initializeStetho(implicit contextSupport: ContextSupport): TaskService[Unit] =\n    TaskService {\n      CatchAll[ExternalServicesProcessException] {\n        if (IsStethoActive.readValueWith(contextSupport.context)) {\n          AppLog.info(\"Initializing Stetho\")\n          Stetho.initialize(\n            Stetho\n              .newInitializerBuilder(contextSupport.context)\n              .enableDumpapp(Stetho.defaultDumperPluginsProvider(contextSupport.context))\n              .enableWebKitInspector(\n                Stetho.defaultInspectorModulesProvider(contextSupport.context))\n              .build())\n        }\n      }\n    }\n\n  def initializeFirebase(implicit contextSupport: ContextSupport): TaskService[Unit] =\n    TaskService {\n      CatchAll[ExternalServicesProcessException] {\n        if (readFlag(R.string.firebase_enabled)) {\n          AppLog.info(\"Initializing Firebase\")\n          FirebaseAnalytics.getInstance(contextSupport.context)\n        }\n      }\n    }\n\n  def initializeFlowUp(implicit contextSupport: ContextSupport): TaskService[Unit] =\n    Ui {\n      if (readFlag(R.string.flowup_enabled)) {\n        AppLog.info(\"Initializing FlowUp\")\n        FlowUp.Builder\n          .`with`(contextSupport.application)\n          .apiKey(getString(R.string.flowup_apikey))\n          .start()\n      }\n    }.toService()\n\n  def initializeApptentive(implicit contextSupport: ContextSupport): TaskService[Unit] =\n    Ui {\n      if (readFlag(R.string.apptentive_enabled)) {\n        AppLog.info(\"Initializing Apptentive\")\n        Apptentive.register(contextSupport.application, getString(R.string.apptentive_apikey))\n      }\n    }.toService()\n\n  def readFirebaseToken: TaskService[String] = TaskService {\n    CatchAll[TokenFirebaseException] {\n      FirebaseInstanceId.getInstance().getToken\n    }\n  }\n\n  private[this] def getString(key: Int)(implicit contextSupport: ContextSupport): String =\n    contextSupport.getResources.getString(key)\n\n  private[this] def readFlag(key: Int)(implicit contextSupport: ContextSupport): Boolean =\n    getString(key).equalsIgnoreCase(\"true\")\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/analytics/impl/AnalyticsTrackServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.analytics.impl\n\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.services.TaskService\nimport cards.nine.models.TrackEvent\nimport cards.nine.services.track.{\n  ImplicitsTrackServicesException,\n  TrackServices,\n  TrackServicesException\n}\nimport com.google.android.gms.analytics.{HitBuilders, Tracker}\n\nclass AnalyticsTrackServices(tracker: Tracker)\n    extends TrackServices\n    with ImplicitsTrackServicesException {\n\n  override def trackEvent(event: TrackEvent) = TaskService {\n    CatchAll[TrackServicesException] {\n      tracker.setScreenName(event.screen.name)\n      val eventBuilder = new HitBuilders.EventBuilder()\n      eventBuilder.setCategory(event.category.name)\n      eventBuilder.setAction(event.action.name)\n      event.label foreach eventBuilder.setLabel\n      event.value.map(_.value) foreach eventBuilder.setValue\n      tracker.send(eventBuilder.build())\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/awareness/impl/GoogleAwarenessServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.awareness.impl\n\nimport android.annotation.SuppressLint\nimport android.app.PendingIntent\nimport android.content.{BroadcastReceiver, Intent, IntentFilter}\nimport android.location.{Address, Geocoder}\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models._\nimport cards.nine.models.types._\nimport cards.nine.services.awareness._\nimport cats.syntax.either._\nimport com.google.android.gms.awareness.Awareness\nimport com.google.android.gms.awareness.fence.{\n  AwarenessFence,\n  DetectedActivityFence,\n  FenceUpdateRequest,\n  HeadphoneFence\n}\nimport com.google.android.gms.awareness.snapshot.{\n  DetectedActivityResult,\n  HeadphoneStateResult,\n  LocationResult,\n  WeatherResult\n}\nimport com.google.android.gms.awareness.state.{HeadphoneState, Weather}\nimport com.google.android.gms.common.api.{GoogleApiClient, ResultCallback, Status}\nimport monix.eval.Task\nimport monix.execution.Cancelable\nimport scala.concurrent.duration._\n\nimport scala.util.Success\n\nclass GoogleAwarenessServicesImpl(client: GoogleApiClient) extends AwarenessServices {\n\n  val timeoutAfter = 3.seconds\n\n  override def getTypeActivity =\n    TaskService {\n      Task\n        .async[AwarenessException Either ProbablyActivity] { (scheduler, callback) =>\n          Awareness.SnapshotApi\n            .getDetectedActivity(client)\n            .setResultCallback(new ResultCallback[DetectedActivityResult]() {\n              override def onResult(detectedActivityResult: DetectedActivityResult): Unit = {\n                Option(detectedActivityResult) match {\n                  case Some(result) if result.getStatus.isSuccess =>\n                    Option(result.getActivityRecognitionResult) match {\n                      case Some(recognition)\n                          if Option(recognition.getMostProbableActivity).isDefined =>\n                        callback(Success(Either.right(ProbablyActivity(\n                          KindActivity(recognition.getMostProbableActivity.getType)))))\n                      case _ =>\n                        callback(Success(\n                          Either.left(AwarenessException(\"Most probable activity not found\"))))\n                    }\n                  case _ =>\n                    callback(\n                      Success(Either.left(AwarenessException(\"Detected activity not found\"))))\n                }\n              }\n            })\n\n          Cancelable.empty\n        }\n        .timeoutTo(\n          timeoutAfter,\n          Task.now(Either.left(AwarenessException(\"Timeout trying get type activity\"))))\n    }\n\n  override def registerFenceUpdates(\n      action: String,\n      fences: Seq[AwarenessFenceUpdate],\n      receiver: BroadcastReceiver)(implicit contextSupport: ContextSupport) = {\n\n    def registerReceiver: TaskService[Unit] = TaskService {\n      Task {\n        Either\n          .catchNonFatal(\n            contextSupport.context.registerReceiver(receiver, new IntentFilter(action)))\n          .map(_ => (): Unit)\n          .leftMap(e => AwarenessException(e.getMessage, Some(e)))\n      }\n    }\n\n    def registerIntent: TaskService[Unit] = {\n\n      val pendingIntent =\n        PendingIntent.getBroadcast(contextSupport.context, 1001, new Intent(action), 0)\n      val builder = new FenceUpdateRequest.Builder()\n      fences flatMap toAPIFence foreach {\n        case (apiFence, key) => builder.addFence(key, apiFence, pendingIntent)\n      }\n      val request = builder.build()\n\n      TaskService {\n        Task.async[AwarenessException Either Unit] { (scheduler, callback) =>\n          Awareness.FenceApi\n            .updateFences(client, request)\n            .setResultCallback(new ResultCallback[Status] {\n              override def onResult(r: Status): Unit = {\n                Option(r) match {\n                  case Some(result) if result.getStatus.isSuccess =>\n                    callback(Success(Either.right((): Unit)))\n                  case _ =>\n                    callback(\n                      Success(Either.left(AwarenessException(\"Can't register the fence updates\"))))\n                }\n              }\n            })\n          Cancelable.empty\n        }\n      }\n    }\n\n    for {\n      _ <- registerReceiver\n      _ <- registerIntent\n    } yield ()\n  }\n\n  override def unregisterFenceUpdates(action: String)(implicit contextSupport: ContextSupport) = {\n\n    val request = new FenceUpdateRequest.Builder()\n      .removeFence(PendingIntent.getBroadcast(contextSupport.context, 1001, new Intent(action), 0))\n      .build()\n\n    TaskService {\n      Task.async[AwarenessException Either Unit] { (scheduler, callback) =>\n        Awareness.FenceApi\n          .updateFences(client, request)\n          .setResultCallback(new ResultCallback[Status] {\n            override def onResult(r: Status): Unit = {\n              Option(r) match {\n                case Some(result) if result.getStatus.isSuccess =>\n                  callback(Success(Either.right((): Unit)))\n                case _ =>\n                  callback(\n                    Success(Either.left(AwarenessException(\"Can't register the fence updates\"))))\n              }\n            }\n          })\n        Cancelable.empty\n      }\n    }\n  }\n\n  private[this] def toAPIFence(\n      awarenessFence: AwarenessFenceUpdate): Seq[(AwarenessFence, String)] =\n    awarenessFence match {\n      case HeadphonesFence =>\n        Seq(\n          (HeadphoneFence.during(HeadphoneState.PLUGGED_IN), s\"${HeadphonesFence.keyIn}\"),\n          (HeadphoneFence.during(HeadphoneState.UNPLUGGED), s\"${HeadphonesFence.keyOut}\"))\n      case InVehicleFence =>\n        Seq((DetectedActivityFence.during(DetectedActivityFence.IN_VEHICLE), InVehicleFence.key))\n    }\n\n  override def getHeadphonesState =\n    TaskService {\n      Task\n        .async[AwarenessException Either Headphones] { (scheduler, callback) =>\n          Awareness.SnapshotApi\n            .getHeadphoneState(client)\n            .setResultCallback(new ResultCallback[HeadphoneStateResult]() {\n              override def onResult(headphoneStateResult: HeadphoneStateResult): Unit = {\n                Option(headphoneStateResult) match {\n                  case Some(result) if result.getStatus.isSuccess =>\n                    Option(result.getHeadphoneState) match {\n                      case Some(headphoneState) =>\n                        callback(Success(Either.right(\n                          Headphones(headphoneState.getState == HeadphoneState.PLUGGED_IN))))\n                      case _ =>\n                        callback(\n                          Success(Either.left(AwarenessException(\"Headphone state not found\"))))\n                    }\n                  case _ =>\n                    callback(\n                      Success(Either.left(AwarenessException(\"Headphone result not found\"))))\n                }\n              }\n            })\n          Cancelable.empty\n        }\n        .timeoutTo(\n          timeoutAfter,\n          Task.now(Either.left(AwarenessException(\"Timeout trying get headphone state\"))))\n    }\n\n  override def getLocation(implicit contextSupport: ContextSupport): TaskService[Location] = {\n\n    @SuppressLint(Array(\"NewApi\"))\n    def getCurrentLocation =\n      TaskService {\n        Task\n          .async[AwarenessException Either LocationState] { (scheduler, callback) =>\n            Awareness.SnapshotApi\n              .getLocation(client)\n              .setResultCallback(new ResultCallback[LocationResult]() {\n                override def onResult(locationResult: LocationResult): Unit = {\n                  Option(locationResult) match {\n                    case Some(result) if result.getStatus.isSuccess =>\n                      Option(locationResult.getLocation) match {\n                        case Some(location) =>\n                          val locationState = LocationState(\n                            accuracy = location.getAccuracy,\n                            altitude = location.getAltitude,\n                            bearing = location.getBearing,\n                            latitude = location.getLatitude,\n                            longitude = location.getLongitude,\n                            speed = location.getSpeed,\n                            elapsedTime = location.getElapsedRealtimeNanos,\n                            time = location.getTime)\n                          callback(Success(Either.right(locationState)))\n                        case _ =>\n                          callback(Success(Either.left(AwarenessException(\"Location not found\"))))\n                      }\n                    case _ =>\n                      callback(\n                        Success(Either.left(AwarenessException(\"Location result not found\"))))\n                  }\n                }\n\n              })\n            Cancelable.empty\n          }\n          .timeoutTo(\n            timeoutAfter,\n            Task.now(Either.left(AwarenessException(\"Timeout trying get location\"))))\n      }\n\n    def loadAddress(locationState: LocationState) =\n      TaskService {\n        Task {\n          Either.catchNonFatal {\n            val addressList =\n              new Geocoder(contextSupport.context)\n                .getFromLocation(locationState.latitude, locationState.longitude, 1)\n            Option(addressList) match {\n              case Some(list) if list.size() > 0 =>\n                toAwarenessLocation(list.get(0))\n              case None =>\n                throw new IllegalStateException(\"Geocoder doesn't return a valid address\")\n            }\n          }.leftMap(e => AwarenessException(e.getMessage, Some(e)))\n        }\n      }\n\n    for {\n      locationState <- getCurrentLocation\n      location      <- loadAddress(locationState)\n    } yield location\n  }\n\n  override def getWeather =\n    TaskService {\n      Task\n        .async[AwarenessException Either WeatherState] { (scheduler, callback) =>\n          Awareness.SnapshotApi\n            .getWeather(client)\n            .setResultCallback(new ResultCallback[WeatherResult]() {\n              override def onResult(weatherResult: WeatherResult): Unit = {\n                Option(weatherResult) match {\n                  case Some(result) if result.getStatus.isSuccess =>\n                    Option(weatherResult.getWeather) match {\n                      case Some(weather) =>\n                        val weatherState = WeatherState(\n                          conditions = weather.getConditions map (ConditionWeather(_)),\n                          humidity = weather.getHumidity,\n                          dewPointCelsius = weather.getDewPoint(Weather.CELSIUS),\n                          dewPointFahrenheit = weather.getDewPoint(Weather.FAHRENHEIT),\n                          temperatureCelsius = weather.getTemperature(Weather.CELSIUS),\n                          temperatureFahrenheit = weather.getTemperature(Weather.FAHRENHEIT))\n                        callback(Success(Either.right(weatherState)))\n                      case _ =>\n                        callback(Success(Either.left(AwarenessException(\"Weather not found\"))))\n                    }\n                  case _ =>\n                    callback(Success(Either.left(AwarenessException(\"Weather result not found\"))))\n                }\n              }\n            })\n          Cancelable.empty\n\n        }\n        .timeoutTo(\n          timeoutAfter,\n          Task.now(Either.left(AwarenessException(\"Timeout trying get weather\"))))\n    }\n\n  private[this] def toAwarenessLocation(address: Address) =\n    Location(\n      latitude = address.getLatitude,\n      longitude = address.getLongitude,\n      countryCode = Option(address.getCountryCode),\n      countryName = Option(address.getCountryName),\n      addressLines = toAddressLines(address))\n\n  private[this] def toAddressLines(address: Address) =\n    0 to address.getMaxAddressLineIndex flatMap { index =>\n      Option(address.getAddressLine(index))\n    }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/awareness/impl/Models.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.awareness.impl\n\nimport cards.nine.models.types._\nimport com.google.android.gms.awareness.state.Weather._\nimport com.google.android.gms.location.DetectedActivity._\n\nobject KindActivity {\n\n  def apply(t: Int): KindActivity = t match {\n    case IN_VEHICLE => InVehicleActivity\n    case ON_BICYCLE => OnBicycleActivity\n    case ON_FOOT    => OnFootActivity\n    case RUNNING    => RunningActivity\n    case STILL      => StillActivity\n    case TILTING    => TiltingActivity\n    case WALKING    => WalkingActivity\n    case _          => UnknownActivity\n  }\n\n}\n\nobject ConditionWeather {\n\n  def apply(t: Int): ConditionWeather = t match {\n    case CONDITION_CLEAR  => ClearCondition\n    case CONDITION_CLOUDY => CloudyCondition\n    case CONDITION_FOGGY  => FoggyCondition\n    case CONDITION_HAZY   => HazyCondition\n    case CONDITION_ICY    => IcyCondition\n    case CONDITION_RAINY  => RainyCondition\n    case CONDITION_SNOWY  => SnowyCondition\n    case CONDITION_STORMY => StormyCondition\n    case CONDITION_WINDY  => WindyCondition\n    case _                => UnknownCondition\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/drive/Conversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.drive\n\nimport cards.nine.services.drive.impl.DriveServicesImpl._\nimport cards.nine.services.drive.models.DriveServiceFileSummary\nimport com.google.android.gms.drive.Metadata\n\ntrait Conversions {\n\n  def toGoogleDriveFileSummary(metadata: Metadata): DriveServiceFileSummary =\n    DriveServiceFileSummary(\n      uuid = metadata.getCustomProperties.get(propertyUUID),\n      deviceId = Option(metadata.getCustomProperties.get(propertyDeviceId)),\n      title = metadata.getTitle,\n      createdDate = metadata.getCreatedDate,\n      modifiedDate = metadata.getModifiedDate)\n\n  def toGoogleDriveFileSummary(uuid: String, metadata: Metadata): DriveServiceFileSummary =\n    DriveServiceFileSummary(\n      uuid = uuid,\n      deviceId = Option(metadata.getCustomProperties.get(propertyDeviceId)),\n      title = metadata.getTitle,\n      createdDate = metadata.getCreatedDate,\n      modifiedDate = metadata.getModifiedDate)\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/drive/DriveServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.drive\n\nimport java.io.InputStream\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.services.drive.models.{DriveServiceFile, DriveServiceFileSummary}\nimport com.google.android.gms.common.api.GoogleApiClient\n\ntrait DriveServices {\n\n  /**\n   * Creates the Drive API client\n   * @param account the email for the client\n   * @return the GoogleAPIClient\n   */\n  def createDriveClient(account: String)(\n      implicit contextSupport: ContextSupport): TaskService[GoogleApiClient]\n\n  /**\n   * Return a sequence of files in the user app space filtered by fileType key\n   * @param client the google API client\n   * @param maybeFileType any String or `None` to list all files\n   * @return Sequence of `DriveServiceFile`\n   * @throws DriveServicesException if there was an error with the request GoogleDrive api\n   */\n  def listFiles(\n      client: GoogleApiClient,\n      maybeFileType: Option[String]): TaskService[Seq[DriveServiceFileSummary]]\n\n  /**\n   * Verify if a specific file exists\n   * @param client the google API client\n   * @param driveId file identifier\n   * @return the device name if exists, None if not exists\n   * @throws DriveServicesException if there was an error with the request GoogleDrive api\n   */\n  def fileExists(client: GoogleApiClient, driveId: String): TaskService[Option[String]]\n\n  /**\n   * Returns the content of a file\n   * @param client the google API client\n   * @param driveId that identifies the file to be read\n   * @return the file content as String\n   * @throws DriveServicesException if there was an error with the request GoogleDrive api\n   */\n  def readFile(client: GoogleApiClient, driveId: String): TaskService[DriveServiceFile]\n\n  /**\n   * Creates a new text file\n   * @param client the google API client\n   * @param title the file title\n   * @param content the content as String\n   * @param deviceId custom device identifier\n   * @param fileType a String that later can be used in #listFiles method\n   * @param mimeType the file\n   * @return the file identifier\n   * @throws DriveServicesException if there was an error with the request GoogleDrive api\n   */\n  def createFile(\n      client: GoogleApiClient,\n      title: String,\n      content: String,\n      deviceId: String,\n      fileType: String,\n      mimeType: String): TaskService[DriveServiceFileSummary]\n\n  /**\n   * Creates a new file\n   * @param client the google API client\n   * @param title the file title\n   * @param content the content as InputStream (won't be closed after finish)\n   * @param deviceId custom device identifier\n   * @param fileType a String that later can be used in #listFiles method\n   * @param mimeType the file mimeType\n   * @throws DriveServicesException if there was an error with the request GoogleDrive api\n   */\n  def createFile(\n      client: GoogleApiClient,\n      title: String,\n      content: InputStream,\n      deviceId: String,\n      fileType: String,\n      mimeType: String): TaskService[DriveServiceFileSummary]\n\n  /**\n   * Updates the content of an existing text file\n   * @param client the google API client\n   * @param driveId that identifies the file to be updated\n   * @param title the new title\n   * @param content the content as String\n   * @throws DriveServicesException if there was an error with the request GoogleDrive api\n   */\n  def updateFile(\n      client: GoogleApiClient,\n      driveId: String,\n      title: String,\n      content: String): TaskService[DriveServiceFileSummary]\n\n  /**\n   * Updates the content of an existing file\n   * @param client the google API client\n   * @param driveId that identifies the file to be updated\n   * @param title the new title\n   * @param content the content as InputStream (won't be closed after finish)\n   * @throws DriveServicesException if there was an error with the request GoogleDrive api\n   */\n  def updateFile(\n      client: GoogleApiClient,\n      driveId: String,\n      title: String,\n      content: InputStream): TaskService[DriveServiceFileSummary]\n\n  /**\n   * Try to delete the file\n   * @param client the google API client\n   * @param driveId that identifies the file to be updated\n   * @return Unit\n   * @throws DriveServicesException if there was an error or there is no file with this identifier\n   */\n  def deleteFile(client: GoogleApiClient, driveId: String): TaskService[Unit]\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/drive/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.drive\n\nimport cards.nine.commons.services.TaskService.NineCardException\nimport com.google.android.gms.common.ConnectionResult\n\nsealed trait GoogleDriveError\n\ncase object DriveSigInRequired extends GoogleDriveError\n\ncase object DriveRateLimitExceeded extends GoogleDriveError\n\ncase object DriveResourceNotAvailable extends GoogleDriveError\n\ncase class DriveServicesException(\n    message: String,\n    googleDriveError: Option[GoogleDriveError] = None,\n    cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/drive/impl/DriveServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.drive.impl\n\nimport java.io.{InputStream, OutputStreamWriter}\n\nimport cats.syntax.either._\nimport cards.nine.commons._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.services.drive._\nimport cards.nine.services.drive.impl.DriveServicesImpl._\nimport cards.nine.services.drive.impl.Extensions._\nimport cards.nine.services.drive.models.{DriveServiceFile, DriveServiceFileSummary}\nimport com.google.android.gms.common.api._\nimport com.google.android.gms.drive._\nimport com.google.android.gms.drive.metadata.CustomPropertyKey\nimport com.google.android.gms.drive.query.{Filters, Query, SortOrder, SortableField}\nimport monix.eval.Task\nimport monix.execution.Cancelable\n\nimport scala.collection.JavaConversions._\nimport scala.util.{Failure, Success, Try}\n\nclass DriveServicesImpl extends DriveServices with Conversions {\n\n  private[this] val fileNotFoundError = (driveId: String) =>\n    s\"File with id $driveId doesn't exists\"\n\n  private[this] val queryUUID = (driveId: String) =>\n    new Query.Builder().addFilter(Filters.eq(propertyUUID, driveId)).build()\n\n  override def createDriveClient(account: String)(implicit contextSupport: ContextSupport) =\n    TaskService {\n      Task {\n        Either.catchNonFatal {\n          new GoogleApiClient.Builder(contextSupport.context)\n            .setAccountName(account)\n            .addApi(Drive.API)\n            .addScope(Drive.SCOPE_APPFOLDER)\n            .build()\n        } leftMap { e: Throwable =>\n          DriveServicesException(message = e.getMessage, googleDriveError = None, cause = Some(e))\n        }\n      }\n    }\n\n  override def listFiles(client: GoogleApiClient, maybeFileType: Option[String]) = {\n    val sortOrder = new SortOrder.Builder().addSortAscending(SortableField.MODIFIED_DATE).build()\n    val maybeQuery = maybeFileType map { fileType =>\n      new Query.Builder()\n        .addFilter(Filters.eq(propertyFileType, fileType))\n        .setSortOrder(sortOrder)\n        .build()\n    }\n    searchFiles(client, maybeQuery)(seq => seq)\n  }\n\n  override def fileExists(client: GoogleApiClient, driveId: String) =\n    searchFiles(client, Option(queryUUID(driveId)))(_.headOption.map(_.title))\n\n  override def readFile(client: GoogleApiClient, driveId: String) = {\n\n    def readFileContentsService(driveId: DriveId): TaskService[String] =\n      driveId.asDriveFile.open(client, DriveFile.MODE_READ_ONLY, javaNull).withResultAsync {\n        result =>\n          val contents = result.getDriveContents\n          val stringContent =\n            scala.io.Source.fromInputStream(contents.getInputStream).mkString\n          contents.discard(client)\n          Right(stringContent)\n      }\n\n    for {\n      metadataResult <- fetchDriveFile(client, driveId)\n      content        <- readFileContentsService(metadataResult.metadata.getDriveId)\n    } yield {\n      val summary = toGoogleDriveFileSummary(metadataResult.metadata)\n      tryToClose(metadataResult.buffer)\n      DriveServiceFile(summary, content)\n    }\n\n  }\n\n  override def createFile(\n      client: GoogleApiClient,\n      title: String,\n      content: String,\n      deviceId: String,\n      fileType: String,\n      mimeType: String) =\n    createNewFile(client, newUUID, title, deviceId, fileType, mimeType, _.write(content))\n\n  override def createFile(\n      client: GoogleApiClient,\n      title: String,\n      content: InputStream,\n      deviceId: String,\n      fileType: String,\n      mimeType: String) =\n    createNewFile(\n      client,\n      newUUID,\n      title,\n      deviceId,\n      fileType,\n      mimeType,\n      writer => Iterator.continually(content.read).takeWhile(_ != -1).foreach(writer.write))\n\n  override def updateFile(\n      client: GoogleApiClient,\n      driveId: String,\n      title: String,\n      content: String) =\n    updateFileWith(client, driveId, title, _.write(content))\n\n  override def updateFile(\n      client: GoogleApiClient,\n      driveId: String,\n      title: String,\n      content: InputStream) =\n    updateFileWith(\n      client,\n      driveId,\n      title,\n      writer => Iterator.continually(content.read).takeWhile(_ != -1).foreach(writer.write))\n\n  override def deleteFile(client: GoogleApiClient, driveId: String) = {\n\n    def deleteFileService(driveId: DriveId): TaskService[Unit] =\n      driveId.asDriveFile.delete(client).withResultAsync(_ => Right(Unit))\n\n    for {\n      metadataResult <- fetchDriveFile(client, driveId)\n      _              <- deleteFileService(metadataResult.metadata.getDriveId)\n      _ = tryToClose(metadataResult.buffer)\n    } yield ()\n  }\n\n  private[this] def newUUID = com.gilt.timeuuid.TimeUuid().toString\n\n  private[this] def searchFiles[R](client: GoogleApiClient, query: Option[Query])(\n      f: (Seq[DriveServiceFileSummary]) => R) = {\n\n    def searchFiles: TaskService[MetadataBuffer] = {\n      val request = query match {\n        case Some(q) => appFolder(client).queryChildren(client, q)\n        case _       => appFolder(client).listChildren(client)\n      }\n      request.withResultAsync { result =>\n        Right(result.getMetadataBuffer)\n      }\n    }\n\n    def splitFiles(buffer: MetadataBuffer): (List[Metadata], List[Metadata]) =\n      buffer.iterator().toIterable.toList.partition { metadata =>\n        Option(metadata.getCustomProperties.get(propertyUUID)).nonEmpty\n      }\n\n    def fixUUID(files: List[Metadata]): TaskService[List[DriveServiceFileSummary]] = {\n      /*\n       * TODO - Remove this block as part of ticket 525 (https://github.com/47deg/nine-cards-v2/issues/525)\n       * This code fixes actual devices using Google Drive\n       */\n      val tasks = files map { metadata =>\n        val uuid = newUUID\n        val changeSet =\n          new MetadataChangeSet.Builder().setCustomProperty(propertyUUID, uuid).build()\n        metadata.getDriveId\n          .asDriveResource()\n          .updateMetadata(client, changeSet)\n          .withResultAsync { result =>\n            Right(toGoogleDriveFileSummary(uuid, result.getMetadata).copy(uuid = uuid))\n          }\n          .value\n      }\n\n      TaskService {\n        Task.gatherUnordered(tasks) map { list =>\n          Right(list.collect {\n            case Right(r) => r\n          })\n        }\n      }\n    }\n\n    for {\n      buffer <- searchFiles\n      (validFiles, filesToFix) = splitFiles(buffer)\n      fixedFiles <- fixUUID(filesToFix)\n    } yield {\n      val summaryFiles = validFiles map toGoogleDriveFileSummary\n      tryToClose(buffer)\n      f(summaryFiles ++ fixedFiles)\n    }\n  }\n\n  private[this] def appFolder(client: GoogleApiClient) =\n    Drive.DriveApi.getAppFolder(client)\n\n  private[this] def createNewFile(\n      client: GoogleApiClient,\n      uuid: String,\n      title: String,\n      deviceId: String,\n      fileType: String,\n      mimeType: String,\n      f: (OutputStreamWriter) => Unit): TaskService[DriveServiceFileSummary] = {\n\n    def createNewFileService: TaskService[DriveContents] =\n      Drive.DriveApi\n        .newDriveContents(client)\n        .withResultAsync(result => Right(result.getDriveContents))\n\n    def writeContents(contents: DriveContents): TaskService[DriveServiceFileSummary] = {\n      val changeSet = new MetadataChangeSet.Builder()\n        .setTitle(title)\n        .setMimeType(mimeType)\n        .setCustomProperty(propertyUUID, uuid)\n        .setCustomProperty(propertyDeviceId, deviceId)\n        .setCustomProperty(propertyFileType, fileType)\n        .build()\n\n      val writer = new OutputStreamWriter(contents.getOutputStream)\n      f(writer)\n      writer.close()\n\n      appFolder(client).createFile(client, changeSet, contents).withResultAsync { result =>\n        val now = new java.util.Date\n        Right(\n          DriveServiceFileSummary(\n            uuid = uuid,\n            deviceId = Some(deviceId),\n            title = title,\n            createdDate = now,\n            modifiedDate = now))\n      }\n    }\n\n    for {\n      contents <- createNewFileService\n      summary  <- writeContents(contents)\n    } yield summary\n  }\n\n  private[this] def updateFileWith(\n      client: GoogleApiClient,\n      driveId: String,\n      title: String,\n      f: (OutputStreamWriter) => Unit): TaskService[DriveServiceFileSummary] = {\n\n    def changeTitleService(driveId: DriveId): TaskService[Metadata] = {\n      val changeSet = new MetadataChangeSet.Builder().setTitle(title).build()\n      driveId\n        .asDriveResource()\n        .updateMetadata(client, changeSet)\n        .withResultAsync(result => Right(result.getMetadata))\n    }\n\n    def writeContentsService(driveId: DriveId): TaskService[DriveContents] =\n      driveId.asDriveFile.open(client, DriveFile.MODE_WRITE_ONLY, javaNull).withResultAsync {\n        result =>\n          val contents = result.getDriveContents\n          val writer   = new OutputStreamWriter(contents.getOutputStream)\n          f(writer)\n          writer.close()\n          Right(contents)\n      }\n\n    for {\n      metadataResult <- fetchDriveFile(client, driveId)\n      newMetadata    <- changeTitleService(metadataResult.metadata.getDriveId)\n      summary = toGoogleDriveFileSummary(newMetadata)\n      contents <- writeContentsService(newMetadata.getDriveId)\n      _        <- commitContentsService(client, contents)\n    } yield {\n      tryToClose(metadataResult.buffer)\n      summary\n    }\n  }\n\n  private[this] def tryToClose(metadata: MetadataBuffer): Unit =\n    Try(metadata.release())\n\n  private[this] def commitContentsService(\n      client: GoogleApiClient,\n      contents: DriveContents): TaskService[Unit] =\n    contents.commit(client, javaNull).withResultAsync(_ => Right((): Unit))\n\n  private[this] def fetchDriveFile(\n      client: GoogleApiClient,\n      driveId: String): TaskService[MetadataResult] =\n    appFolder(client).queryChildren(client, queryUUID(driveId)).withResultAsync { r =>\n      val buffer = r.getMetadataBuffer\n      val response = buffer.iterator().toIterable.headOption match {\n        case Some(metaData) => Right(MetadataResult(buffer, metaData))\n        case None =>\n          buffer.release()\n          Left(DriveServicesException(fileNotFoundError(driveId)))\n      }\n      response\n    }\n\n}\n\nobject DriveServicesImpl {\n\n  private[this] val uuid = \"FILE_UUID\"\n\n  private[this] val customFileType = \"FILE_TYPE\"\n\n  private[this] val customDeviceId = \"FILE_ID\"\n\n  def propertyUUID = new CustomPropertyKey(uuid, CustomPropertyKey.PRIVATE)\n\n  def propertyFileType =\n    new CustomPropertyKey(customFileType, CustomPropertyKey.PRIVATE)\n\n  def propertyDeviceId =\n    new CustomPropertyKey(customDeviceId, CustomPropertyKey.PRIVATE)\n\n  case class MetadataResult(buffer: MetadataBuffer, metadata: Metadata)\n\n}\n\nobject Extensions {\n\n  implicit class PendingResultOps[T <: Result](pendingResult: PendingResult[T]) {\n\n    def withResultAsync[R](f: (T) => Either[DriveServicesException, R]): TaskService[R] =\n      withResultAsync(f, None)\n\n    def withResultAsync[R](\n        f: (T) => Either[DriveServicesException, R],\n        validCodesAndDefault: Option[(Seq[Int], R)]): TaskService[R] = {\n\n      def statusCodeToError(statusCode: Int) = statusCode match {\n        case CommonStatusCodes.SIGN_IN_REQUIRED => Option(DriveSigInRequired)\n        case DriveStatusCodes.DRIVE_RATE_LIMIT_EXCEEDED =>\n          Option(DriveRateLimitExceeded)\n        case DriveStatusCodes.DRIVE_RESOURCE_NOT_AVAILABLE =>\n          Option(DriveResourceNotAvailable)\n        case _ => None\n      }\n\n      TaskService {\n        Task.async[DriveServicesException Either R] { (scheduler, callback) =>\n          pendingResult.setResultCallback(new ResultCallback[T] {\n            override def onResult(callbackResult: T): Unit = {\n              (Option(callbackResult), validCodesAndDefault) match {\n                case (Some(result), _) if result.getStatus.isSuccess =>\n                  Try(f(result)) match {\n                    case Success(r) => callback(Success(r))\n                    case Failure(e) =>\n                      callback(\n                        Success(Left(DriveServicesException(e.getMessage, cause = Some(e)))))\n                  }\n                case (Some(result), Some((validCodes, defaultValue)))\n                    if validCodes contains result.getStatus.getStatusCode =>\n                  callback(Success(Right(defaultValue)))\n                case (Some(result), _) =>\n                  callback(\n                    Success(\n                      Left(DriveServicesException(\n                        googleDriveError = statusCodeToError(result.getStatus.getStatusCode),\n                        message = result.getStatus.getStatusMessage))))\n                case _ =>\n                  callback(\n                    Success(\n                      Left(DriveServicesException(\n                        message = \"Received a null reference in pending result\",\n                        cause = Option(new NullPointerException())))))\n              }\n            }\n          })\n          Cancelable.empty\n        }\n      }\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/drive/models/DriveServicesModels.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.drive.models\n\nimport java.util.Date\n\ncase class DriveServiceFileSummary(\n    uuid: String,\n    deviceId: Option[String],\n    title: String,\n    createdDate: Date,\n    modifiedDate: Date)\n\ncase class DriveServiceFile(summary: DriveServiceFileSummary, content: String)\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/permissions/impl/AndroidSupportPermissionsServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.permissions.impl\n\nimport android.app.Activity\nimport android.content.pm.PackageManager\nimport android.support.v4.app.ActivityCompat\nimport android.support.v4.content.ContextCompat\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contexts.{ActivityContextSupport, ContextSupport}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.models.types.{PermissionDenied, PermissionGranted}\nimport cards.nine.services.permissions._\n\nclass AndroidSupportPermissionsServices\n    extends PermissionsServices\n    with ImplicitsPermissionsServicesExceptions {\n\n  override def checkPermissions(permissions: Seq[String])(\n      implicit contextSupport: ContextSupport) =\n    TaskService {\n      CatchAll[PermissionsServicesException] {\n        permissions.map { permission =>\n          val status =\n            ContextCompat.checkSelfPermission(contextSupport.context, permission) match {\n              case PackageManager.PERMISSION_GRANTED => PermissionGranted\n              case _                                 => PermissionDenied\n            }\n          permission -> status\n        }.toMap\n      }\n    }\n\n  override def shouldShowRequestPermissions(permissions: Seq[String])(\n      implicit contextSupport: ActivityContextSupport) =\n    TaskService {\n      CatchAll[PermissionsServicesException] {\n        withActivity { activity =>\n          permissions.map { permission =>\n            permission -> ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)\n          }.toMap\n        }\n      }\n    }\n\n  override def requestPermissions(requestCode: Int, permissions: Seq[String])(\n      implicit contextSupport: ActivityContextSupport) =\n    TaskService {\n      CatchAll[PermissionsServicesException] {\n        withActivity { activity =>\n          ActivityCompat.requestPermissions(activity, permissions.toArray, requestCode)\n        }\n      }\n    }\n\n  def withActivity[T](f: (Activity) => T)(implicit contextSupport: ActivityContextSupport): T =\n    contextSupport.getActivity match {\n      case Some(activity) => f(activity)\n      case None =>\n        throw new IllegalStateException(\"Activity not found in the context\")\n    }\n\n  override def readPermissionsRequestResult(permissions: Seq[String], grantResults: Seq[Int]) =\n    TaskService {\n      CatchAll[PermissionsServicesException] {\n        ((permissions zip grantResults) map {\n          case (permission, PackageManager.PERMISSION_GRANTED) =>\n            permission -> PermissionGranted\n          case (permission, _) => permission -> PermissionDenied\n        }).toMap\n      }\n    }\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/plus/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.plus\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class GooglePlusServicesException(\n    message: String,\n    cause: Option[Throwable] = None,\n    recoverable: Boolean = false)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsGooglePlusProcessExceptions {\n\n  implicit def googlePlusExceptionConverter =\n    (t: Throwable) => GooglePlusServicesException(t.getMessage, Option(t))\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/plus/GooglePlusServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.plus\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.services.plus.models.GooglePlusProfile\nimport com.google.android.gms.common.api.GoogleApiClient\n\ntrait GooglePlusServices {\n\n  /**\n   * Creates the Google Plus API client\n   * @param clientId the OAuth Client Id for requesting the token Id\n   * @param account the email for the client\n   * @return the GoogleAPIClient\n   */\n  def createGooglePlusClient(clientId: String, account: String)(\n      implicit contextSupport: ContextSupport): TaskService[GoogleApiClient]\n\n  /**\n   * Load the user information for Google Plus\n   * @param client the google API client\n   * @return the information about the profile\n   */\n  def loadUserProfile(client: GoogleApiClient): TaskService[GooglePlusProfile]\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/plus/impl/GooglePlusServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.plus.impl\n\nimport cats.syntax.either._\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.services.plus.models.GooglePlusProfile\nimport cards.nine.services.plus._\nimport com.google.android.gms.auth.api.Auth\nimport com.google.android.gms.auth.api.signin.{GoogleSignInOptions, GoogleSignInStatusCodes}\nimport com.google.android.gms.common.api.{CommonStatusCodes, GoogleApiClient, ResultCallback}\nimport com.google.android.gms.plus.People.LoadPeopleResult\nimport com.google.android.gms.plus.Plus\nimport com.google.android.gms.plus.model.people.Person\nimport monix.eval.Task\nimport monix.execution.Cancelable\n\nimport scala.util.{Failure, Success, Try}\n\nclass GooglePlusServicesImpl extends GooglePlusServices with ImplicitsGooglePlusProcessExceptions {\n\n  val me = \"me\"\n\n  val recoverableStatusCodes: Seq[Int] = Seq(\n    CommonStatusCodes.API_NOT_CONNECTED,\n    CommonStatusCodes.CANCELED,\n    CommonStatusCodes.INTERRUPTED,\n    CommonStatusCodes.INVALID_ACCOUNT,\n    CommonStatusCodes.RESOLUTION_REQUIRED,\n    CommonStatusCodes.SIGN_IN_REQUIRED,\n    GoogleSignInStatusCodes.SIGN_IN_CANCELLED,\n    GoogleSignInStatusCodes.SIGN_IN_FAILED)\n\n  val validCodes: Seq[Int] =\n    Seq(CommonStatusCodes.SUCCESS, CommonStatusCodes.SUCCESS_CACHE)\n\n  override def createGooglePlusClient(clientId: String, account: String)(\n      implicit contextSupport: ContextSupport) =\n    TaskService {\n      Task {\n        Either.catchNonFatal {\n          val gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)\n            .requestScopes(Plus.SCOPE_PLUS_PROFILE)\n            .requestIdToken(clientId)\n            .setAccountName(account)\n            .build()\n\n          new GoogleApiClient.Builder(contextSupport.context)\n            .addApi(Auth.GOOGLE_SIGN_IN_API, gso)\n            .addApi(Plus.API)\n            .build()\n        } leftMap { e: Throwable =>\n          GooglePlusServicesException(e.getMessage, Some(e))\n        }\n      }\n\n    }\n\n  override def loadUserProfile(client: GoogleApiClient) = {\n\n    def resultCallback(\n        result: LoadPeopleResult): Either[GooglePlusServicesException, LoadPeopleResult] =\n      Option(result) match {\n        case Some(r) if validCodes.contains(r.getStatus.getStatusCode) =>\n          Right(result)\n        case Some(r) =>\n          val message = Option(r.getStatus.getStatusMessage) getOrElse \"Unknown error with Google API\"\n          Left(\n            GooglePlusServicesException(\n              message = message,\n              recoverable = recoverableStatusCodes.contains(r.getStatus.getStatusCode)))\n        case _ =>\n          Left(\n            GooglePlusServicesException(\n              message = \"Received null on the result\",\n              recoverable = false))\n      }\n\n    def loadPeopleApi: TaskService[LoadPeopleResult] = TaskService {\n      Task.async[GooglePlusServicesException Either LoadPeopleResult] { (scheduler, callback) =>\n        Try(Plus.PeopleApi.load(client, me)) match {\n          case Success(pr) =>\n            pr.setResultCallback(new ResultCallback[LoadPeopleResult] {\n              override def onResult(r: LoadPeopleResult): Unit =\n                callback(Success(resultCallback(r)))\n            })\n          case Failure(ex) =>\n            callback(\n              Success(\n                Left(\n                  GooglePlusServicesException(\n                    message = Option(ex.getMessage) getOrElse \"Error loading people API\",\n                    cause = Some(ex),\n                    recoverable = false))))\n        }\n        Cancelable.empty\n      }\n    }\n\n    def notNullOrThrow[T](value: T, message: String): T = Option(value) match {\n      case Some(v) => v\n      case None    => throw new IllegalStateException(message)\n    }\n\n    def nonEmpty(string: String): Option[String] =\n      Option(string).find(_.nonEmpty)\n\n    def fetchPerson(loadPeopleResult: LoadPeopleResult): TaskService[Person] =\n      TaskService {\n        CatchAll[GooglePlusServicesException] {\n          val people =\n            notNullOrThrow(loadPeopleResult, \"LoadPeopleResult is null\")\n          val personBuffer =\n            notNullOrThrow(people.getPersonBuffer, \"PersonBuffer on LoadPeopleResult is null\")\n          if (personBuffer.getCount > 0) {\n            notNullOrThrow(personBuffer.get(0), \"Person in PersonBuffer is null\")\n          } else {\n            throw new IllegalStateException(\"There aren't any persons in the PersonBuffer\")\n          }\n        }\n      }\n\n    def fetchName(person: Person): Option[String] = {\n      val directNames =\n        List(nonEmpty(person.getNickname), nonEmpty(person.getDisplayName))\n      val personNames = Option(person.getName).toList flatMap { name =>\n        List(nonEmpty(name.getGivenName), nonEmpty(name.getFamilyName))\n      }\n      (directNames ++ personNames).flatten.headOption\n    }\n\n    def fetchAvatarUrl(person: Person): Option[String] = {\n      Option(person.getImage) flatMap { image =>\n        nonEmpty(image.getUrl)\n      }\n    }\n\n    def fetchCoverUrl(person: Person): Option[String] =\n      for {\n        cover      <- Option(person.getCover)\n        coverPhoto <- Option(cover.getCoverPhoto)\n        coverUrl   <- nonEmpty(coverPhoto.getUrl)\n      } yield coverUrl\n\n    (for {\n      loadPeopleResult <- loadPeopleApi\n      person           <- fetchPerson(loadPeopleResult)\n      name      = fetchName(person)\n      avatarUrl = fetchAvatarUrl(person)\n      coverUrl  = fetchCoverUrl(person)\n    } yield GooglePlusProfile(name, avatarUrl, coverUrl)).resolve[GooglePlusServicesException]\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/main/scala/cards/nine/services/plus/models/Model.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.plus.models\n\ncase class GooglePlusProfile(\n    name: Option[String],\n    avatarUrl: Option[String],\n    coverUrl: Option[String])\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/observers/ObserverRegisterSpecification.scala",
    "content": "package cards.nine.app.observers\n\nimport android.content.ContentResolver\nimport android.database.ContentObserver\nimport android.net.Uri\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.contentresolver.UriCreator\nimport cards.nine.commons.contexts.ContextSupport\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait ObserverRegisterSpecification extends Specification with Mockito {\n\n  trait ObserverRegisterScope extends Scope {\n\n    lazy implicit val contextSupport = mock[ContextSupport]\n\n    lazy val contextResolver = mock[ContentResolver]\n\n    lazy val uriCreator = mock[UriCreator]\n\n    lazy val mockUri = mock[Uri]\n\n    lazy val observerRegister = new ObserverRegister(uriCreator)\n\n  }\n\n}\n\nclass ObserverRegisterSpec extends ObserverRegisterSpecification {\n\n  \"ObserverRegister\" should {\n\n    \"call to register observer with the right params\" in new ObserverRegisterScope {\n\n      uriCreator.parse(any) returns mockUri\n      contextSupport.getContentResolver returns contextResolver\n\n      observerRegister.registerObserverTask().value.run\n\n      there was one(contextResolver)\n        .registerContentObserver(any[Uri], any[Boolean], any[ContentObserver])\n\n    }\n\n    \"call to unregister observer with the right params\" in new ObserverRegisterScope {\n\n      uriCreator.parse(any) returns mockUri\n      contextSupport.getContentResolver returns contextResolver\n\n      observerRegister.unregisterObserverTask().value.run\n\n      there was one(contextResolver).unregisterContentObserver(any[ContentObserver])\n\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/receivers/bluetooth/BluetoothContextSupport.scala",
    "content": "package cards.nine.app.receivers.bluetooth\n\nimport android.app.Application\nimport android.content.Context\nimport cards.nine.app.commons.{ContextSupportImpl, ContextSupportPreferences}\n\nimport scala.ref.WeakReference\n\nclass BluetoothContextSupport extends ContextSupportImpl with ContextSupportPreferences {\n\n  override def addBluetoothDevice(device: String): Unit = {}\n\n  override def removeBluetoothDevice(device: String): Unit = {}\n\n  override def clearBluetoothDevices(): Unit = {}\n\n  override def application: Application = ???\n\n  override def context: Context = ???\n\n  override def getOriginal: WeakReference[Context] = ???\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/receivers/bluetooth/BluetoothJobsSpecification.scala",
    "content": "package cards.nine.app.receivers.bluetooth\n\nimport cards.nine.app.ui.commons.BroadAction\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport macroid.ContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait BluetoothJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait BluetoothJobsScope extends Scope {\n\n    implicit val contextWrapper = mock[ContextWrapper]\n\n    lazy implicit val mockContextSupport = mock[BluetoothContextSupport]\n\n    val bluetoothJobs = new BluetoothJobs {\n      override implicit def contextSupport(implicit ctx: ContextWrapper): ContextSupport =\n        mockContextSupport\n      override def sendBroadCastTask(broadAction: BroadAction): TaskService[Unit] =\n        TaskService.empty\n    }\n\n  }\n\n}\n\nclass BluetoothJobsSpec extends BluetoothJobsSpecification {\n\n  \"BluetoothJobs\" should {\n\n    \"call to add bluetooth device in Context Support\" in new BluetoothJobsScope {\n      val device = \"My Bluetooth Device\"\n\n      bluetoothJobs.addBluetoothDevice(device).mustRightUnit\n\n      there was one(mockContextSupport).addBluetoothDevice(device)\n\n    }\n\n    \"call to remove bluetooth device in Context Support\" in new BluetoothJobsScope {\n      val device = \"My Bluetooth Device\"\n\n      bluetoothJobs.removeBluetoothDevice(device).mustRightUnit\n\n      there was one(mockContextSupport).removeBluetoothDevice(device)\n\n    }\n\n    \"call to remove all devices connected in Context Support\" in new BluetoothJobsScope {\n      val device = \"My Bluetooth Device\"\n\n      bluetoothJobs.removeAllBluetoothDevices().mustRightUnit\n\n      there was one(mockContextSupport).clearBluetoothDevices()\n\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/applinks/AppLinksReceiverJobsSpec.scala",
    "content": "package cards.nine.app.ui.applinks\n\nimport cards.nine.app.di.Injector\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.{ApplicationTestData, SharedCollectionTestData}\nimport cards.nine.models.types.{GetByName, PublishedByMe, PublishedByOther}\nimport cards.nine.process.collection.CollectionProcess\nimport cards.nine.process.device.DeviceProcess\nimport cards.nine.process.intents.LauncherExecutorProcess\nimport cards.nine.process.sharedcollections.SharedCollectionsProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait AppLinksReceiverJobsSpecification\n    extends TaskServiceSpecification\n    with Mockito\n    with SharedCollectionTestData\n    with ApplicationTestData {\n\n  trait AppLinksReceiverJobsScope extends Scope {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector = mock[Injector]\n\n    val mockAppLinksReceiverUiActions = mock[AppLinksReceiverUiActions]\n\n    val mockLauncherExecutorProcess = mock[LauncherExecutorProcess]\n\n    mockInjector.launcherExecutorProcess returns mockLauncherExecutorProcess\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val mockCollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockSharedCollectionsProcess = mock[SharedCollectionsProcess]\n\n    mockInjector.sharedCollectionsProcess returns mockSharedCollectionsProcess\n\n    val appLinksReceiverJobs =\n      new AppLinksReceiverJobs(mockAppLinksReceiverUiActions)(contextWrapper) {\n\n        override lazy val di: Injector = mockInjector\n\n        override def getString(res: Int, args: AnyRef*): String = \"\"\n\n      }\n  }\n}\n\nclass AppLinksReceiverJobsSpec extends AppLinksReceiverJobsSpecification {\n\n  \"addCollection\" should {\n\n    \"return a valid response and call to subscribe when the service returns a right\" in new AppLinksReceiverJobsScope {\n\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n      mockCollectionProcess.addCollection(any) returns serviceRight(collection)\n      mockAppLinksReceiverUiActions.exit() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.subscribe(any)(any) returns serviceRight(Unit)\n\n      appLinksReceiverJobs\n        .addCollection(sharedCollection.copy(publicCollectionStatus = PublishedByOther))\n        .mustRightUnit\n\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n      there was one(mockAppLinksReceiverUiActions).exit()\n      there was one(mockCollectionProcess).addCollection(any)\n      there was one(mockSharedCollectionsProcess).subscribe(\n        ===(sharedCollection.sharedCollectionId))(any)\n    }\n\n    \"doesn't call to subscribe when the status is PublishedByMe\" in new AppLinksReceiverJobsScope {\n\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n      mockCollectionProcess.addCollection(any) returns serviceRight(collection)\n      mockAppLinksReceiverUiActions.exit() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.subscribe(any)(any) returns serviceRight(Unit)\n\n      appLinksReceiverJobs\n        .addCollection(sharedCollection.copy(publicCollectionStatus = PublishedByMe))\n        .mustRightUnit\n\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n      there was one(mockAppLinksReceiverUiActions).exit()\n      there was one(mockCollectionProcess).addCollection(any)\n      there was no(mockSharedCollectionsProcess).subscribe(any)(any)\n    }\n  }\n\n  \"showError\" should {\n    \"shows an unexpected error message\" in new AppLinksReceiverJobsScope {\n\n      mockAppLinksReceiverUiActions.showUnexpectedErrorMessage() returns serviceRight(Unit)\n      mockAppLinksReceiverUiActions.exit() returns serviceRight(Unit)\n\n      appLinksReceiverJobs.showError().mustRightUnit\n\n      there was one(mockAppLinksReceiverUiActions).exit()\n      there was one(mockAppLinksReceiverUiActions).showUnexpectedErrorMessage()\n    }\n  }\n\n  \"shareCollection\" should {\n    \"return a valid response when the service returns a right response\" in new AppLinksReceiverJobsScope {\n\n      mockLauncherExecutorProcess.launchShare(any)(any) returns serviceRight(Unit)\n      mockAppLinksReceiverUiActions.exit() returns serviceRight(Unit)\n\n      appLinksReceiverJobs.shareCollection(sharedCollection).mustRightUnit\n\n      there was one(mockAppLinksReceiverUiActions).exit()\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/collections/jobs/GroupCollectionsJobsSpecification.scala",
    "content": "package cards.nine.app.ui.collections.jobs\n\nimport android.content.Intent\nimport cards.nine.app.di.Injector\nimport cards.nine.app.observers.ObserverRegister\nimport cards.nine.app.ui.collections.CollectionsDetailsActivity.statuses\nimport cards.nine.app.ui.collections.jobs.uiactions.{\n  GroupCollectionsDOM,\n  GroupCollectionsUiActions,\n  NavigationUiActions,\n  ToolbarUiActions\n}\nimport cards.nine.app.ui.commons.{BroadAction, JobException, RequestCodes, UiException}\nimport cards.nine.app.ui.launcher.jobs.LauncherTestData\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.CollectionTestData\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.models.types.{CallPhone, PermissionResult, ReadCallLog}\nimport cards.nine.process.accounts.UserAccountsProcess\nimport cards.nine.process.collection.{CardException, CollectionException, CollectionProcess}\nimport cards.nine.process.device.DeviceProcess\nimport cards.nine.process.intents.LauncherExecutorProcess\nimport cards.nine.process.moment.MomentProcess\nimport cards.nine.process.theme.ThemeProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\nimport cards.nine.app.ui.collections.CollectionsDetailsActivity._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.commons.test.data.CardValues._\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.models.Card\n\ntrait GroupCollectionsJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait GroupCollectionsJobsScope extends Scope with CollectionTestData with LauncherTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector = mock[Injector]\n\n    val mockGroupCollectionsDOM = mock[GroupCollectionsDOM]\n\n    val mockGroupCollectionsUiActions = mock[GroupCollectionsUiActions]\n\n    mockGroupCollectionsUiActions.dom returns mockGroupCollectionsDOM\n\n    val mockNavigationUiActions = mock[NavigationUiActions]\n\n    val mockToolbarUiActions = mock[ToolbarUiActions]\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockCollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val mockMomentProcess = mock[MomentProcess]\n\n    mockInjector.momentProcess returns mockMomentProcess\n\n    val mockThemeProcess = mock[ThemeProcess]\n\n    mockInjector.themeProcess returns mockThemeProcess\n\n    val mockObserverRegister = mock[ObserverRegister]\n\n    mockInjector.observerRegister returns mockObserverRegister\n\n    val mockUserAccountsProcess = mock[UserAccountsProcess]\n\n    mockInjector.userAccountsProcess returns mockUserAccountsProcess\n\n    val mockLauncherExecutorProcess = mock[LauncherExecutorProcess]\n\n    mockInjector.launcherExecutorProcess returns mockLauncherExecutorProcess\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val addNewShortcutResponse: TaskService[Option[Card]] = TaskService.right(Some(card))\n\n    val groupCollectionsJobs = new GroupCollectionsJobs(\n      mockGroupCollectionsUiActions,\n      mockToolbarUiActions,\n      mockNavigationUiActions)(contextWrapper) {\n\n      override lazy val di: Injector = mockInjector\n\n      override def themeFile = \"\"\n\n      override def sendBroadCastTask(broadAction: BroadAction) = TaskService.empty\n\n      override def addNewShortcut(collectionId: Int, data: Intent): TaskService[Option[Card]] =\n        addNewShortcutResponse\n    }\n\n  }\n\n}\n\nclass GroupCollectionsJobsSpec extends GroupCollectionsJobsSpecification {\n\n  \"initialize\" should {\n    \"shows the collections when the service returns a right response\" in new GroupCollectionsJobsScope {\n\n      mockToolbarUiActions.initialize(any, any, any, any) returns serviceRight(Unit)\n      mockThemeProcess.getTheme(any)(any) returns serviceRight(theme)\n      mockGroupCollectionsUiActions.initialize() returns serviceRight(Unit)\n      mockCollectionProcess.getCollections returns serviceRight(seqCollection)\n      mockGroupCollectionsUiActions.showCollections(any, any) returns serviceRight(Unit)\n\n      groupCollectionsJobs\n        .initialize(backgroundColor, initialToolbarColor, icon, position, stateChanged)\n        .mustRightUnit\n\n      there was one(mockToolbarUiActions)\n        .initialize(backgroundColor, initialToolbarColor, icon, stateChanged)\n      there was one(mockGroupCollectionsUiActions).showCollections(seqCollection, position)\n\n    }\n\n    \"return a CollectionException if the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      mockToolbarUiActions.initialize(any, any, any, any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.initialize() returns serviceRight(Unit)\n      mockThemeProcess.getTheme(any)(any) returns serviceRight(theme)\n      mockCollectionProcess.getCollections returns serviceLeft(CollectionException(\"\"))\n\n      groupCollectionsJobs\n        .initialize(backgroundColor, initialToolbarColor, icon, position, stateChanged)\n        .mustLeft[CollectionException]\n\n      there was one(mockToolbarUiActions)\n        .initialize(backgroundColor, initialToolbarColor, icon, stateChanged)\n      there was no(mockGroupCollectionsUiActions).showCollections(seqCollection, position)\n\n    }\n  }\n\n  \"resume\" should {\n    \"calls to register Observer\" in new GroupCollectionsJobsScope {\n\n      mockObserverRegister.registerObserverTask() returns serviceRight(Unit)\n      groupCollectionsJobs.resume().mustRightUnit\n      there was one(mockObserverRegister).registerObserverTask()\n    }\n  }\n\n  \"pause\" should {\n    \"calls to unregister Observer\" in new GroupCollectionsJobsScope {\n\n      mockObserverRegister.unregisterObserverTask() returns serviceRight(Unit)\n      groupCollectionsJobs.pause().mustRightUnit\n      there was one(mockObserverRegister).unregisterObserverTask()\n    }\n  }\n\n  \"back\" should {\n    \"calls to back\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.back() returns serviceRight(Unit)\n      groupCollectionsJobs.back().mustRightUnit\n      there was one(mockGroupCollectionsUiActions).back()\n    }\n  }\n\n  \"destroy\" should {\n    \"calls to destroy\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.destroy() returns serviceRight(Unit)\n      groupCollectionsJobs.destroy().mustRightUnit\n      there was one(mockGroupCollectionsUiActions).destroy()\n    }\n  }\n\n  \"reloadCards\" should {\n    \"reloads cards when the service returns a right response \" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(Option(collection))\n      mockGroupCollectionsUiActions.reloadCards(any) returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n\n      groupCollectionsJobs.reloadCards() mustRight { r =>\n        r shouldEqual seqCard\n      }\n\n      there was one(mockCollectionProcess).getCollectionById(collection.id)\n      there was one(mockGroupCollectionsUiActions).reloadCards(seqCard)\n    }\n\n    \"reloads card the database when current the collection cards are different\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(\n        Option(collection.copy(cards = Seq(card(3), card(3), card(5)))))\n      mockGroupCollectionsUiActions.reloadCards(any) returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n\n      groupCollectionsJobs.reloadCards() mustRight { r =>\n        r shouldEqual Seq(card(3), card(3), card(5))\n      }\n\n      there was one(mockCollectionProcess).getCollectionById(collection.id)\n      there was one(mockGroupCollectionsUiActions).reloadCards(Seq(card(3), card(3), card(5)))\n    }\n\n    \"return a UiException when the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n      groupCollectionsJobs.reloadCards().mustLeft[UiException]\n      there was no(mockCollectionProcess).getCollectionById(any)\n    }\n\n    \"return a CollectionException when the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockCollectionProcess.getCollectionById(any) returns serviceLeft(CollectionException(\"\"))\n\n      groupCollectionsJobs.reloadCards().mustLeft[CollectionException]\n\n      there was one(mockGroupCollectionsUiActions).getCurrentCollection\n      there was one(mockCollectionProcess).getCollectionById(collection.id)\n    }\n  }\n\n  sequential\n  \"editCard\" should {\n    \"call to edit card when the service returns a right response\" in new GroupCollectionsJobsScope {\n\n      statuses = statuses.copy(positionsEditing = Set(1))\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockGroupCollectionsUiActions.editCard(any, any, any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.closeEditingModeUi() returns serviceRight(Unit)\n\n      groupCollectionsJobs.editCard().mustRightUnit\n\n    }\n    \"return a JobException because you only can edit one card\" in new GroupCollectionsJobsScope {\n\n      statuses = statuses.copy(positionsEditing = Set(1, 2))\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      groupCollectionsJobs.editCard().mustLeft[JobException]\n    }\n\n    \"return a JobException when the current positionsEditing of statuses is Set.empty\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      groupCollectionsJobs.editCard().mustLeft[JobException]\n    }\n\n    \"return a UiException when the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n      groupCollectionsJobs.editCard().mustLeft[UiException]\n    }\n  }\n\n  \"removeCardsInEditMode\" should {\n    \"call to remove cards when the service returns a right response\" in new GroupCollectionsJobsScope {\n\n      statuses = statuses.copy(positionsEditing = Set(0, 1, 2))\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockGroupCollectionsUiActions.closeEditingModeUi() returns serviceRight(Unit)\n      mockTrackEventProcess.removeApplications(any) returns serviceRight(Unit)\n      mockCollectionProcess.deleteCards(any, any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.removeCards(any) returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n\n      groupCollectionsJobs.removeCardsInEditMode() mustRight {\n        _ shouldEqual Seq(card(0), card(1), card(2))\n      }\n\n      there was one(mockGroupCollectionsUiActions).getCurrentCollection\n      there was one(mockTrackEventProcess).removeApplications(cardPackageSeq)\n      there was one(mockCollectionProcess).deleteCards(collection.id, cardIdSeq)\n      there was one(mockGroupCollectionsUiActions).removeCards(seqCard)\n      there was one(mockMomentProcess).getMoments\n    }\n\n    \"return a UiException when the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n      groupCollectionsJobs.removeCardsInEditMode().mustLeft[UiException]\n      there was one(mockGroupCollectionsUiActions).getCurrentCollection\n    }\n  }\n\n  \"removeCardsByPackagesName\" should {\n    \"remove cards by packages name when the service right response\" in new GroupCollectionsJobsScope {\n\n      mockTrackEventProcess.removeAppsByFab(any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockTrackEventProcess.removeApplications(any) returns serviceRight(Unit)\n      mockCollectionProcess.deleteCards(any, any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.removeCards(any) returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n\n      groupCollectionsJobs.removeCardsByPackagesName(cardPackageSeq) mustRight (_ shouldEqual seqCard)\n\n      there was one(mockTrackEventProcess).removeAppsByFab(cardPackageSeq)\n      there was one(mockGroupCollectionsUiActions).getCurrentCollection\n      there was one(mockTrackEventProcess).removeApplications(cardPackageSeq)\n      there was one(mockCollectionProcess).deleteCards(collection.id, cardIdSeq)\n      there was one(mockGroupCollectionsUiActions).removeCards(seqCard)\n      there was one(mockMomentProcess).getMoments\n    }\n\n    \"Do nothing when not found cards by packages name, cards is Seq.empty\" in new GroupCollectionsJobsScope {\n\n      mockTrackEventProcess.removeAppsByFab(any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockTrackEventProcess.removeApplications(any) returns serviceRight(Unit)\n      mockCollectionProcess.deleteCards(any, any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.removeCards(any) returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n\n      groupCollectionsJobs.removeCardsByPackagesName(Seq.empty) mustRight (_ shouldEqual Seq.empty)\n\n      there was one(mockTrackEventProcess).removeAppsByFab(Seq.empty)\n      there was one(mockGroupCollectionsUiActions).getCurrentCollection\n      there was one(mockTrackEventProcess).removeApplications(Seq.empty)\n      there was one(mockCollectionProcess).deleteCards(collection.id, Seq.empty)\n      there was one(mockGroupCollectionsUiActions).removeCards(Seq.empty)\n      there was one(mockMomentProcess).getMoments\n    }\n\n    \"return a UiException when the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      mockTrackEventProcess.removeAppsByFab(any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n      groupCollectionsJobs.removeCardsByPackagesName(cardPackageSeq).mustLeft[UiException]\n      there was one(mockGroupCollectionsUiActions).getCurrentCollection\n    }\n  }\n\n  \"removeCards\" should {\n    \"remove cards when the services returns right response\" in new GroupCollectionsJobsScope {\n\n      mockTrackEventProcess.removeApplications(any) returns serviceRight(Unit)\n      mockCollectionProcess.deleteCards(any, any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.removeCards(any) returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n\n      groupCollectionsJobs.removeCards(collection.id, seqCard) mustRight (_ shouldEqual seqCard)\n\n      there was one(mockTrackEventProcess).removeApplications(cardPackageSeq)\n      there was one(mockCollectionProcess).deleteCards(collection.id, cardIdSeq)\n      there was one(mockGroupCollectionsUiActions).removeCards(seqCard)\n      there was one(mockMomentProcess).getMoments\n    }\n\n    \"return a CardException when the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      mockTrackEventProcess.removeApplications(any) returns serviceRight(Unit)\n      mockCollectionProcess.deleteCards(any, any) returns serviceLeft(CardException(\"\"))\n\n      groupCollectionsJobs.removeCards(collection.id, seqCard).mustLeft[CardException]\n\n      there was one(mockTrackEventProcess).removeApplications(cardPackageSeq)\n      there was one(mockCollectionProcess).deleteCards(collection.id, cardIdSeq)\n      there was no(mockGroupCollectionsUiActions).removeCards(any)\n      there was no(mockMomentProcess).getMoments\n    }\n  }\n\n  \"moveToCollection\" should {\n    \"move to collection when the service returns right response\" in new GroupCollectionsJobsScope {\n\n      statuses = statuses.copy(positionsEditing = Set(0, 1, 2))\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockTrackEventProcess.moveApplications(any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.getCollection(any) returns serviceRight(Option(collection))\n      mockGroupCollectionsUiActions.closeEditingModeUi() returns serviceRight(Unit)\n      mockCollectionProcess.deleteCards(any, any) returns serviceRight(Unit)\n      mockCollectionProcess.addCards(any, any) returns serviceRight(seqCard)\n      mockGroupCollectionsUiActions.removeCards(any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.addCardsToCollection(any, any) returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n\n      groupCollectionsJobs\n        .moveToCollection(collection.id, collection.position) mustRight (_ shouldEqual seqCard)\n\n      there was one(mockGroupCollectionsUiActions).getCurrentCollection\n      there was one(mockTrackEventProcess).moveApplications(collection.name)\n      there was one(mockGroupCollectionsUiActions).getCollection(position)\n      there was one(mockGroupCollectionsUiActions).closeEditingModeUi()\n      there was one(mockCollectionProcess).deleteCards(collection.id, cardIdSeq)\n      there was one(mockCollectionProcess).addCards(collection.id, seqCardData)\n      there was one(mockGroupCollectionsUiActions).removeCards(seqCard)\n      there was one(mockGroupCollectionsUiActions)\n        .addCardsToCollection(collection.position, seqCard)\n      there was one(mockMomentProcess).getMoments\n    }\n\n    \"return a UiException when the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      statuses = statuses.copy(positionsEditing = Set(0, 1, 2))\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n\n      groupCollectionsJobs\n        .moveToCollection(collection.id, collection.position)\n        .mustLeft[UiException]\n\n      there was one(mockGroupCollectionsUiActions).getCurrentCollection\n    }\n  }\n\n  \"savePublishStatus\" should {\n    \"save publish status the current collection\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n\n      groupCollectionsJobs.savePublishStatus().mustRightUnit\n\n      statuses.publishStatus shouldEqual collection.publicCollectionStatus\n    }\n\n    \"return a UiException when the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n      groupCollectionsJobs.savePublishStatus().mustLeft[UiException]\n      there was no(mockCollectionProcess).getCollectionById(any)\n    }\n  }\n\n  sequential\n  \"performCard\" should {\n    \"call to launcher executor when collectionMode is NormalCollection\" in new GroupCollectionsJobsScope {\n\n      statuses = statuses.copy(collectionMode = NormalCollectionMode)\n      mockLauncherExecutorProcess.execute(any)(any) returns serviceRight(Unit)\n      mockGroupCollectionsDOM.getCurrentCollection returns Option(collection)\n      mockTrackEventProcess.openAppFromCollection(any, any) returns serviceRight(Unit)\n      mockMomentProcess.getMomentByCollectionId(any) returns serviceRight(Option(moment))\n\n      groupCollectionsJobs.performCard(card, card.position).mustRightUnit\n\n      there was one(mockGroupCollectionsDOM).getCurrentCollection\n      there was two(mockTrackEventProcess)\n        .openAppFromCollection(===(card.packageName.getOrElse(\"\")), any)\n      there was one(mockMomentProcess).getMomentByCollectionId(collection.id)\n    }\n\n    \"call to launcher executor when collectionMode is NormalCollection and don't track  if doesn't have a moment.\" in new GroupCollectionsJobsScope {\n\n      statuses = statuses.copy(collectionMode = NormalCollectionMode)\n      mockLauncherExecutorProcess.execute(any)(any) returns serviceRight(Unit)\n      mockGroupCollectionsDOM.getCurrentCollection returns Option(collection)\n      mockTrackEventProcess.openAppFromCollection(any, any) returns serviceRight(Unit)\n      mockMomentProcess.getMomentByCollectionId(any) returns serviceRight(None)\n\n      groupCollectionsJobs.performCard(card.copy(packageName = None), card.position).mustRightUnit\n\n      there was one(mockGroupCollectionsDOM).getCurrentCollection\n      there was one(mockTrackEventProcess).openAppFromCollection(===(\"\"), any)\n      there was one(mockMomentProcess).getMomentByCollectionId(collection.id)\n    }\n\n    \"call to launcher executor when collectionMode is NormalCollection\" in new GroupCollectionsJobsScope {\n\n      statuses = statuses.copy(collectionMode = NormalCollectionMode)\n      mockLauncherExecutorProcess.execute(any)(any) returns serviceRight(Unit)\n      mockGroupCollectionsDOM.getCurrentCollection returns Option(collection)\n      mockTrackEventProcess.openAppFromCollection(any, any) returns serviceRight(Unit)\n      mockMomentProcess.getMomentByCollectionId(any) returns serviceRight(Option(moment))\n\n      groupCollectionsJobs.performCard(card, card.position).mustRightUnit\n\n      there was one(mockGroupCollectionsDOM).getCurrentCollection\n      there was two(mockTrackEventProcess)\n        .openAppFromCollection(===(card.packageName.getOrElse(\"\")), any)\n      there was one(mockMomentProcess).getMomentByCollectionId(collection.id)\n    }\n\n    \"call to launcher executor when collectionMode is NormalCollection\" in new GroupCollectionsJobsScope {\n\n      statuses = statuses.copy(collectionMode = NormalCollectionMode)\n      mockLauncherExecutorProcess.execute(any)(any) returns serviceRight(Unit)\n      mockGroupCollectionsDOM.getCurrentCollection returns None\n\n      groupCollectionsJobs.performCard(card, card.position).mustRightUnit\n\n      there was one(mockGroupCollectionsDOM).getCurrentCollection\n      there was no(mockTrackEventProcess)\n        .openAppFromCollection(===(card.packageName.getOrElse(\"\")), any)\n      there was no(mockMomentProcess).getMomentByCollectionId(any)\n    }\n\n    \"call to reloadItemCollection when collectionMode is EditingCollectionMode\" in new GroupCollectionsJobsScope {\n\n      statuses =\n        statuses.copy(collectionMode = EditingCollectionMode, positionsEditing = Set(0, 1, 2))\n      mockGroupCollectionsUiActions.reloadItemCollection(any, any) returns serviceRight(Unit)\n\n      groupCollectionsJobs.performCard(card, card.position).mustRightUnit\n      there was one(mockGroupCollectionsUiActions).reloadItemCollection(2, card.position)\n\n    }\n\n    \"call to closeEditingModeUi when collectionMode is EditingCollectionMode and positions editing are empty\" in new GroupCollectionsJobsScope {\n\n      statuses = statuses.copy(collectionMode = EditingCollectionMode, positionsEditing = Set(1))\n      mockGroupCollectionsUiActions.closeEditingModeUi() returns serviceRight(Unit)\n\n      groupCollectionsJobs.performCard(card, card.position).mustRightUnit\n\n      there was one(mockGroupCollectionsUiActions).closeEditingModeUi\n    }\n  }\n\n  \"requestCallPhonePermission\" should {\n    \"call to requestPermissions\" in new GroupCollectionsJobsScope {\n\n      mockUserAccountsProcess.requestPermission(any, any)(any) returns serviceRight(Unit)\n      groupCollectionsJobs.requestCallPhonePermission(Option(numberPhone)).mustRightUnit\n      there was one(mockUserAccountsProcess)\n        .requestPermission(===(RequestCodes.phoneCallPermission), ===(CallPhone))(any)\n    }\n  }\n\n  \"requestPermissionsResult\" should {\n\n    \"Do nothing if requestCode is differnt phoneCallPermission\" in new GroupCollectionsJobsScope {\n\n      groupCollectionsJobs\n        .requestPermissionsResult(\n          RequestCodes.callLogPermission,\n          Array(ReadCallLog.value),\n          Array.empty)\n        .mustRightUnit\n    }\n\n    \"call to launcherExecutorProcess for the specified permissions: phoneCallPermission \" in new GroupCollectionsJobsScope {\n\n      mockUserAccountsProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(PermissionResult(CallPhone, result = true)))\n      statuses = statuses.copy(lastPhone = Option(lastPhone))\n      mockLauncherExecutorProcess.execute(any)(any) returns serviceRight(Unit)\n\n      groupCollectionsJobs\n        .requestPermissionsResult(\n          RequestCodes.phoneCallPermission,\n          Array(CallPhone.value),\n          Array.empty)\n        .mustRightUnit\n    }\n\n    \"Do nothing for the specified permissions :phoneCallPermission if hasn't lastPhone\" in new GroupCollectionsJobsScope {\n\n      mockUserAccountsProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(PermissionResult(CallPhone, result = true)))\n      statuses = statuses.copy(lastPhone = None)\n      groupCollectionsJobs\n        .requestPermissionsResult(\n          RequestCodes.phoneCallPermission,\n          Array(CallPhone.value),\n          Array.empty)\n        .mustRightUnit\n    }\n\n    \"Show a message error if haven't permissions phoneCallPermission \" in new GroupCollectionsJobsScope {\n\n      mockUserAccountsProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(PermissionResult(CallPhone, result = false)))\n      statuses = statuses.copy(lastPhone = Option(lastPhone))\n      mockLauncherExecutorProcess.launchDial(any)(any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.showNoPhoneCallPermissionError() returns serviceRight(Unit)\n\n      groupCollectionsJobs\n        .requestPermissionsResult(\n          RequestCodes.phoneCallPermission,\n          Array(CallPhone.value),\n          Array.empty)\n        .mustRightUnit\n    }\n\n    \"Do nothing for the specified permissions :phoneCallPermission if hasn't lastPhone \" in new GroupCollectionsJobsScope {\n\n      mockUserAccountsProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(PermissionResult(CallPhone, result = false)))\n      statuses = statuses.copy(lastPhone = None)\n\n      groupCollectionsJobs\n        .requestPermissionsResult(\n          RequestCodes.phoneCallPermission,\n          Array(CallPhone.value),\n          Array.empty)\n        .mustRightUnit\n    }\n  }\n\n  \"addCards\" should {\n    \"added cards when the service retursn right response\" in new GroupCollectionsJobsScope {\n\n      mockTrackEventProcess.addAppsByFab(any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockCollectionProcess.addCards(any, any) returns serviceRight(seqCard)\n      mockGroupCollectionsUiActions.addCards(any) returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n\n      groupCollectionsJobs.addCards(seqCardData) mustRight (_ shouldEqual seqCard)\n\n      there was one(mockTrackEventProcess).addAppsByFab(cardDataPackageSeq)\n      there was one(mockGroupCollectionsUiActions).getCurrentCollection\n      there was one(mockGroupCollectionsUiActions).addCards(seqCard)\n      there was one(mockCollectionProcess).addCards(collection.id, seqCardData)\n    }\n\n    \"return a UiException when the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      mockTrackEventProcess.addAppsByFab(any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n\n      groupCollectionsJobs.addCards(seqCardData).mustLeft[UiException]\n\n      there was one(mockTrackEventProcess).addAppsByFab(cardDataPackageSeq)\n      there was one(mockGroupCollectionsUiActions).getCurrentCollection\n    }\n  }\n\n  \"addShortcut\" should {\n    \"added shortcut when the service retursn right response\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockTrackEventProcess.addShortcutByFab(any) returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.addCards(any) returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n\n      groupCollectionsJobs.addShortcut(jsonToNineCardIntent(intent)) mustRight (_ shouldEqual Some(\n        card))\n\n      there was one(mockTrackEventProcess).addShortcutByFab(term)\n      there was one(mockGroupCollectionsUiActions).addCards(Seq(card))\n    }\n\n    \"return a UiException when the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n\n      groupCollectionsJobs.addShortcut(jsonToNineCardIntent(intent)).mustLeft[UiException]\n\n      there was no(mockTrackEventProcess).addShortcutByFab(any)\n    }\n  }\n\n  sequential\n  \"openReorderMode\" should {\n    \"call to closeEditingMode and openReorderMode when statuses mode is EditingCollectionMode\" in new GroupCollectionsJobsScope {\n\n      statuses = statuses.copy(collectionMode = EditingCollectionMode)\n      mockGroupCollectionsUiActions.closeEditingModeUi() returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.openReorderModeUi() returns serviceRight(Unit)\n\n      groupCollectionsJobs.openReorderMode().mustRightUnit\n\n      there was one(mockGroupCollectionsUiActions).closeEditingModeUi()\n      there was one(mockGroupCollectionsUiActions).openReorderModeUi()\n    }\n\n    \"call to openReorderMode and modified the statuses to EditingCollectionMode when statuses mode is NormalCollectionMode\" in new GroupCollectionsJobsScope {\n\n      statuses = statuses.copy(collectionMode = NormalCollectionMode)\n      mockGroupCollectionsUiActions.openReorderModeUi() returns serviceRight(Unit)\n\n      groupCollectionsJobs.openReorderMode().mustRightUnit\n\n      there was no(mockGroupCollectionsUiActions).closeEditingModeUi()\n      there was one(mockGroupCollectionsUiActions).openReorderModeUi()\n    }\n  }\n\n  \"closeReorderMode\" should {\n    \"call to closeReorderMode\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.startEditing(any) returns serviceRight(Unit)\n      groupCollectionsJobs.closeReorderMode(position).mustRightUnit\n      there was one(mockGroupCollectionsUiActions).startEditing(position)\n    }\n  }\n\n  \"closeEditingMode\" should {\n    \"call to closeEditingMode\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.closeEditingModeUi() returns serviceRight(Unit)\n      groupCollectionsJobs.closeEditingMode().mustRightUnit\n      there was one(mockGroupCollectionsUiActions).closeEditingModeUi()\n    }\n  }\n\n  \"emptyCollection\" should {\n    \"shows menu when the service returns a right response\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockGroupCollectionsUiActions.showMenu(any, any, any) returns serviceRight(Unit)\n\n      groupCollectionsJobs.emptyCollection.mustRightUnit\n\n      there was one(mockGroupCollectionsUiActions)\n        .showMenu(false, false, collection.themedColorIndex)\n    }\n\n    \"return a UiException when the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n      groupCollectionsJobs.emptyCollection.mustLeft[UiException]\n      there was no(mockGroupCollectionsUiActions).showMenu(any, any, any)\n    }\n  }\n\n  \"firstItemInCollection\" should {\n    \"call to hide menu button\" in new GroupCollectionsJobsScope {\n\n      mockGroupCollectionsUiActions.hideMenuButton() returns serviceRight(Unit)\n      groupCollectionsJobs.firstItemInCollection().mustRightUnit\n      there was one(mockGroupCollectionsUiActions).hideMenuButton()\n    }\n  }\n\n  \"close\" should {\n    \"call action close\" in new GroupCollectionsJobsScope {\n\n      mockTrackEventProcess.closeCollectionByGesture() returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.close() returns serviceRight(Unit)\n      groupCollectionsJobs.close().mustRightUnit\n    }\n  }\n\n  \"showMenu\" should {\n    \"shows menu when the service returns a right response\" in new GroupCollectionsJobsScope {\n\n      mockTrackEventProcess.addCardByMenu() returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockGroupCollectionsUiActions.showMenu(any, any, any) returns serviceRight(Unit)\n\n      groupCollectionsJobs.showMenu(true).mustRightUnit\n\n      there was one(mockGroupCollectionsUiActions)\n        .showMenu(true, true, collection.themedColorIndex)\n    }\n\n    \"return a UiException when the service throws an exception\" in new GroupCollectionsJobsScope {\n\n      mockTrackEventProcess.addCardByMenu() returns serviceRight(Unit)\n      mockGroupCollectionsUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n\n      groupCollectionsJobs.showMenu(true).mustLeft[UiException]\n\n      there was no(mockGroupCollectionsUiActions).showMenu(any, any, any)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/collections/jobs/NavigationJobsSpecification.scala",
    "content": "package cards.nine.app.ui.collections.jobs\n\nimport android.os.Bundle\nimport cards.nine.app.ui.collections.jobs.uiactions.{\n  GroupCollectionsDOM,\n  GroupCollectionsUiActions,\n  NavigationUiActions\n}\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.CollectionTestData\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait NavigationJobsSpecification\n    extends TaskServiceSpecification\n    with Mockito\n    with CollectionTestData {\n\n  trait NavigationJobsScope extends Scope {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    implicit val mockGroupCollectionsJobs = mock[GroupCollectionsJobs]\n\n    implicit val mockSingleCollectionJobs = mock[SingleCollectionJobs]\n\n    val mockGroupCollectionsDOM = mock[GroupCollectionsDOM]\n\n    val mockGroupCollectionsUiActions = mock[GroupCollectionsUiActions]\n\n    val mockNavigationUiActions = mock[NavigationUiActions]\n\n    mockNavigationUiActions.dom returns mockGroupCollectionsDOM\n\n    val mockBundle = mock[Bundle]\n\n    val navigationJobs =\n      new NavigationJobs(mockGroupCollectionsUiActions, mockNavigationUiActions)(contextWrapper)\n\n  }\n}\n\nclass NavigationJobsSpec extends NavigationJobsSpecification {\n\n  \"showAppDialog\" should {\n    \"call to openApps\" in new NavigationJobsScope {\n\n      mockGroupCollectionsDOM.getCurrentCollection returns Option(collection)\n      mockNavigationUiActions.openApps(any)(any, any) returns serviceRight((): Unit)\n\n      navigationJobs\n        .showAppDialog()(mockGroupCollectionsJobs, Option(mockSingleCollectionJobs))\n        .mustRightUnit\n\n      there was two(mockGroupCollectionsDOM).getCurrentCollection\n      there was one(mockNavigationUiActions).openApps(any)(\n        ===(mockGroupCollectionsJobs),\n        ===(Option(mockSingleCollectionJobs)))\n    }\n\n    \"call to openApps when current collection is None\" in new NavigationJobsScope {\n\n      mockGroupCollectionsDOM.getCurrentCollection returns None\n      mockNavigationUiActions.openApps(any)(any, any) returns serviceRight((): Unit)\n\n      navigationJobs\n        .showAppDialog()(mockGroupCollectionsJobs, Option(mockSingleCollectionJobs))\n        .mustRightUnit\n\n      there was two(mockGroupCollectionsDOM).getCurrentCollection\n      there was one(mockNavigationUiActions).openApps(any)(\n        ===(mockGroupCollectionsJobs),\n        ===(Option(mockSingleCollectionJobs)))\n    }\n  }\n\n  \"showRecommendationDialog\" should {\n\n    \"call to openRecommendations\" in new NavigationJobsScope {\n\n      mockGroupCollectionsDOM.getCurrentCollection returns Option(collection)\n      mockNavigationUiActions.openRecommendations(any)(any, any) returns serviceRight((): Unit)\n\n      navigationJobs\n        .showRecommendationDialog()(mockGroupCollectionsJobs, Option(mockSingleCollectionJobs))\n        .mustRightUnit\n\n      there was two(mockGroupCollectionsDOM).getCurrentCollection\n      there was one(mockNavigationUiActions).openRecommendations(any)(\n        ===(mockGroupCollectionsJobs),\n        ===(Option(mockSingleCollectionJobs)))\n    }\n\n    \"shows a message of ContactUsError when current collection doesn't have appCategory and their cards doesn't have packageName\" in new NavigationJobsScope {\n\n      mockGroupCollectionsDOM.getCurrentCollection returns Option(\n        collection\n          .copy(appsCategory = None, cards = collection.cards.map(_.copy(packageName = None))))\n      mockGroupCollectionsUiActions.showContactUsError() returns serviceRight((): Unit)\n\n      navigationJobs\n        .showRecommendationDialog()(mockGroupCollectionsJobs, Option(mockSingleCollectionJobs))\n        .mustRightUnit\n\n      there was one(mockGroupCollectionsDOM).getCurrentCollection\n      there was one(mockGroupCollectionsUiActions).showContactUsError()\n    }\n\n    \"shows a message of ContactUsError when current collection is None\" in new NavigationJobsScope {\n\n      mockGroupCollectionsDOM.getCurrentCollection returns None\n      mockGroupCollectionsUiActions.showContactUsError() returns serviceRight((): Unit)\n\n      navigationJobs\n        .showRecommendationDialog()(mockGroupCollectionsJobs, Option(mockSingleCollectionJobs))\n        .mustRightUnit\n\n      there was one(mockGroupCollectionsDOM).getCurrentCollection\n      there was one(mockGroupCollectionsUiActions).showContactUsError()\n    }\n  }\n\n  \"showContactsDialog\" should {\n    \"call to openContacts\" in new NavigationJobsScope {\n\n      mockNavigationUiActions.openContacts(any)(any, any) returns serviceRight((): Unit)\n      mockGroupCollectionsDOM.getCurrentCollection returns Option(collection)\n\n      navigationJobs\n        .showContactsDialog()(mockGroupCollectionsJobs, Option(mockSingleCollectionJobs))\n        .mustRightUnit\n      there was one(mockNavigationUiActions).openContacts(any)(\n        ===(mockGroupCollectionsJobs),\n        ===(Option(mockSingleCollectionJobs)))\n    }\n\n    \"call to openContacts when current collection is None\" in new NavigationJobsScope {\n\n      mockNavigationUiActions.openContacts(any)(any, any) returns serviceRight((): Unit)\n      mockGroupCollectionsDOM.getCurrentCollection returns None\n\n      navigationJobs\n        .showContactsDialog()(mockGroupCollectionsJobs, Option(mockSingleCollectionJobs))\n        .mustRightUnit\n      there was one(mockNavigationUiActions).openContacts(any)(\n        ===(mockGroupCollectionsJobs),\n        ===(Option(mockSingleCollectionJobs)))\n    }\n  }\n\n  \"showShortcutDialog\" should {\n    \"call to openShortcuts\" in new NavigationJobsScope {\n\n      mockNavigationUiActions.openShortcuts(any)(any, any) returns serviceRight((): Unit)\n      mockGroupCollectionsDOM.getCurrentCollection returns Option(collection)\n      mockSingleCollectionJobs.saveCollectionIdForShortcut() returns serviceRight((): Unit)\n\n      navigationJobs\n        .showShortcutDialog()(mockGroupCollectionsJobs, Option(mockSingleCollectionJobs))\n        .mustRightUnit\n      there was one(mockNavigationUiActions).openShortcuts(any)(\n        ===(mockGroupCollectionsJobs),\n        ===(Option(mockSingleCollectionJobs)))\n    }\n\n    \"call to openShortcuts when current collection is None\" in new NavigationJobsScope {\n\n      mockNavigationUiActions.openShortcuts(any)(any, any) returns serviceRight((): Unit)\n      mockGroupCollectionsDOM.getCurrentCollection returns None\n      mockSingleCollectionJobs.saveCollectionIdForShortcut() returns serviceRight((): Unit)\n\n      navigationJobs\n        .showShortcutDialog()(mockGroupCollectionsJobs, Option(mockSingleCollectionJobs))\n        .mustRightUnit\n      there was one(mockNavigationUiActions).openShortcuts(any)(\n        ===(mockGroupCollectionsJobs),\n        ===(Option(mockSingleCollectionJobs)))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/collections/jobs/ShareCollectionJobsSpecification.scala",
    "content": "package cards.nine.app.ui.collections.jobs\n\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.collections.jobs.uiactions.SharedCollectionUiActions\nimport cards.nine.app.ui.commons.UiException\nimport cards.nine.app.ui.commons.ops.CollectionOps._\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.CollectionTestData\nimport cards.nine.models.types.PhoneCardType\nimport cards.nine.process.collection.{CollectionException, CollectionProcess}\nimport cards.nine.process.intents.LauncherExecutorProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport macroid.{ActivityContextWrapper, ContextWrapper}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait ShareCollectionJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait ShareCollectionScope extends Scope with CollectionTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector = mock[Injector]\n\n    val mockSharedCollectionUiActions = mock[SharedCollectionUiActions]\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockCollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val mockLauncherExecutorProcess = mock[LauncherExecutorProcess]\n\n    mockInjector.launcherExecutorProcess returns mockLauncherExecutorProcess\n\n    val shareCollectionJobs =\n      new SharedCollectionJobs(mockSharedCollectionUiActions)(contextWrapper) {\n\n        override lazy val di: Injector = mockInjector\n      }\n  }\n\n}\n\nclass ShareCollectionJobsSpec extends ShareCollectionJobsSpecification {\n\n  \"reloadSharedCollectionId\" should {\n    \"return a valid response when the service returns a right response\" in new ShareCollectionScope {\n\n      mockSharedCollectionUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(Option(collection))\n      mockSharedCollectionUiActions.reloadSharedCollectionId(any) returns serviceRight(Unit)\n\n      shareCollectionJobs.reloadSharedCollectionId().mustRightUnit\n\n      there was one(mockSharedCollectionUiActions).getCurrentCollection\n      there was one(mockCollectionProcess).getCollectionById(collection.id)\n      there was one(mockSharedCollectionUiActions).reloadSharedCollectionId(\n        collection.sharedCollectionId)\n    }\n\n    \"return an CollectionException if the service throws an exception\" in new ShareCollectionScope {\n\n      mockSharedCollectionUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockCollectionProcess.getCollectionById(any) returns serviceLeft(CollectionException(\"\"))\n\n      shareCollectionJobs.reloadSharedCollectionId().mustLeft[CollectionException]\n\n      there was one(mockSharedCollectionUiActions).getCurrentCollection\n      there was one(mockCollectionProcess).getCollectionById(collection.id)\n      there was no(mockSharedCollectionUiActions).reloadSharedCollectionId(any)\n    }\n\n    \"return an UiException if the service throws an exception\" in new ShareCollectionScope {\n\n      mockSharedCollectionUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n\n      shareCollectionJobs.reloadSharedCollectionId().mustLeft[UiException]\n\n      there was one(mockSharedCollectionUiActions).getCurrentCollection\n      there was no(mockCollectionProcess).getCollectionById(any)\n      there was no(mockSharedCollectionUiActions).reloadSharedCollectionId(any)\n    }\n  }\n\n  \"showPublishCollectionWizard\" should {\n    \"shows a message of publish collection when the service returns a right response and  cardType is equal AppCardType\" in new ShareCollectionScope {\n\n      mockSharedCollectionUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockSharedCollectionUiActions.showPublishCollectionWizardDialog(any) returns serviceRight(\n        Unit)\n\n      shareCollectionJobs.showPublishCollectionWizard().mustRightUnit\n\n      there was one(mockSharedCollectionUiActions).getCurrentCollection\n      there was one(mockSharedCollectionUiActions).showPublishCollectionWizardDialog(collection)\n    }\n\n    \"shows a message of error publish collection when the service returns a right response and  cardType isn't equal AppCardType\" in new ShareCollectionScope {\n\n      mockSharedCollectionUiActions.getCurrentCollection returns serviceRight(\n        Option(collection.copy(cards = collection.cards map (_.copy(cardType = PhoneCardType)))))\n      mockSharedCollectionUiActions.showMessagePublishContactsCollectionError returns serviceRight(\n        Unit)\n\n      shareCollectionJobs.showPublishCollectionWizard().mustRightUnit\n\n      there was one(mockSharedCollectionUiActions).getCurrentCollection\n      there was one(mockSharedCollectionUiActions).showMessagePublishContactsCollectionError\n    }\n\n    \"return an UiException if the service throws an exception\" in new ShareCollectionScope {\n\n      mockSharedCollectionUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n      shareCollectionJobs.showPublishCollectionWizard().mustLeft[UiException]\n      there was one(mockSharedCollectionUiActions).getCurrentCollection\n    }\n  }\n\n  \"shareCollection\" should {\n    \"return a valid response when the service returns a right response\" in new ShareCollectionScope {\n\n      mockSharedCollectionUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(Option(collection))\n      mockTrackEventProcess.shareCollectionByMenu(any) returns serviceRight(Unit)\n      mockLauncherExecutorProcess.launchShare(any)(any) returns serviceRight(Unit)\n\n      shareCollectionJobs.shareCollection().mustRightUnit\n\n      there was one(mockSharedCollectionUiActions).getCurrentCollection\n      there was one(mockCollectionProcess).getCollectionById(collection.id)\n    }.pendingUntilFixed\n\n    \"shows a message of error that can't publish collection.\" in new ShareCollectionScope {\n\n      mockSharedCollectionUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(\n        Option(collection.copy(sharedCollectionId = None)))\n      mockSharedCollectionUiActions.showMessageNotPublishedCollectionError returns serviceRight(\n        Unit)\n\n      shareCollectionJobs.shareCollection().mustRightUnit\n\n      there was one(mockSharedCollectionUiActions).getCurrentCollection\n      there was one(mockCollectionProcess).getCollectionById(collection.id)\n      there was one(mockSharedCollectionUiActions).showMessageNotPublishedCollectionError\n    }\n\n    \"return an CollectionException if the service throws an exception\" in new ShareCollectionScope {\n\n      mockSharedCollectionUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockCollectionProcess.getCollectionById(any) returns serviceLeft(CollectionException(\"\"))\n\n      shareCollectionJobs.shareCollection().mustLeft[CollectionException]\n\n      there was one(mockSharedCollectionUiActions).getCurrentCollection\n      there was one(mockCollectionProcess).getCollectionById(collection.id)\n    }\n\n    \"return an UiException if the service throws an exception\" in new ShareCollectionScope {\n      mockSharedCollectionUiActions.getCurrentCollection returns serviceLeft(UiException(\"\"))\n      shareCollectionJobs.shareCollection().mustLeft[UiException]\n      there was one(mockSharedCollectionUiActions).getCurrentCollection\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/collections/jobs/SingleCollectionJobsSpecification.scala",
    "content": "package cards.nine.app.ui.collections.jobs\n\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.collections.jobs.uiactions.SingleCollectionUiActions\nimport cards.nine.app.ui.commons.JobException\nimport cards.nine.app.ui.launcher.jobs.LauncherTestData\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.CollectionTestData\nimport cards.nine.process.collection.{CollectionException, CollectionProcess}\nimport cards.nine.process.theme.ThemeProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.commons.test.data.CardValues._\n\ntrait SingleCollectionJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait SingleCollectionJobsScope extends Scope with CollectionTestData with LauncherTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector = mock[Injector]\n\n    val mockSingleCollectionUiActions = mock[SingleCollectionUiActions]\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockCollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val mockThemeProcess = mock[ThemeProcess]\n\n    mockInjector.themeProcess returns mockThemeProcess\n\n    val mockAnimateCards = true\n\n    val mockCollection = collection\n\n    val singleCollectionJobs = new SingleCollectionJobs(\n      mockAnimateCards,\n      Option(mockCollection),\n      mockSingleCollectionUiActions)(contextWrapper) {\n\n      override lazy val di: Injector = mockInjector\n\n      override def themeFile = \"\"\n\n    }\n\n  }\n\n}\n\nclass SingleCollectionJobsSpec extends SingleCollectionJobsSpecification {\n\n  \"initialize\" should {\n    \"Shows empty collection if it doesn't have a collection\" in new SingleCollectionJobsScope {\n\n      mockThemeProcess.getTheme(any)(any) returns serviceRight(theme)\n      mockSingleCollectionUiActions.initialize(any, any) returns serviceRight(Unit)\n\n      singleCollectionJobs.initialize().mustRightUnit\n\n      there was one(mockSingleCollectionUiActions).initialize(true, mockCollection)\n\n    }\n\n    \"Initializes all actions and services\" in new SingleCollectionJobsScope {\n\n      override val singleCollectionJobs =\n        new SingleCollectionJobs(mockAnimateCards, None, mockSingleCollectionUiActions)(\n          contextWrapper) {\n\n          override lazy val di: Injector = mockInjector\n\n          override def themeFile = \"\"\n\n        }\n\n      mockThemeProcess.getTheme(any)(any) returns serviceRight(theme)\n      mockSingleCollectionUiActions.showEmptyCollection() returns serviceRight(Unit)\n\n      singleCollectionJobs.initialize().mustRightUnit\n\n    }\n  }\n\n  \"reorderCard\" should {\n    \"return a valid response when the service returns a right response\" in new SingleCollectionJobsScope {\n\n      mockTrackEventProcess.reorderApplication(any) returns serviceRight(Unit)\n      mockCollectionProcess.reorderCard(any, any, any) returns serviceRight(Unit)\n      mockSingleCollectionUiActions.reloadCards() returns serviceRight(Unit)\n\n      singleCollectionJobs.reorderCard(collection.id, card.id, position).mustRightUnit\n\n      there was one(mockTrackEventProcess).reorderApplication(position)\n      there was one(mockCollectionProcess).reorderCard(collection.id, card.id, position)\n      there was one(mockSingleCollectionUiActions).reloadCards()\n    }\n  }\n\n  \"moveToCollection\" should {\n    \"return a valid response when the service returns a right response\" in new SingleCollectionJobsScope {\n\n      mockCollectionProcess.getCollections returns serviceRight(seqCollection)\n      mockSingleCollectionUiActions.moveToCollection(any) returns serviceRight(Unit)\n\n      singleCollectionJobs.moveToCollection().mustRightUnit\n\n      there was one(mockCollectionProcess).getCollections\n      there was one(mockSingleCollectionUiActions).moveToCollection(seqCollection)\n    }\n\n    \"return a CollectionException if the service throws an exception\" in new SingleCollectionJobsScope {\n\n      mockCollectionProcess.getCollections returns serviceLeft(CollectionException(\"\"))\n\n      singleCollectionJobs.moveToCollection().mustLeft[CollectionException]\n\n      there was one(mockCollectionProcess).getCollections\n      there was no(mockSingleCollectionUiActions).moveToCollection(any)\n    }\n  }\n\n  \"addCards\" should {\n    \"returns a valid response when the service returns a right response\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockTrackEventProcess.addAppToCollection(any, any) returns serviceRight(Unit)\n      mockSingleCollectionUiActions.addCards(any) returns serviceRight(Unit)\n\n      singleCollectionJobs.addCards(seqCard).mustRightUnit\n\n      there was exactly(seqCard.size)(mockSingleCollectionUiActions).getCurrentCollection\n      there was exactly(seqCard.size)(mockTrackEventProcess).addAppToCollection(any, any)\n      there was one(mockSingleCollectionUiActions).addCards(seqCard)\n    }\n\n    \"returns a valid response but not track nothing when don't have current collection\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.getCurrentCollection returns serviceRight(None)\n      mockSingleCollectionUiActions.addCards(any) returns serviceRight(Unit)\n\n      singleCollectionJobs.addCards(seqCard).mustRightUnit\n\n      there was exactly(seqCard.size)(mockSingleCollectionUiActions).getCurrentCollection\n      there was one(mockSingleCollectionUiActions).addCards(seqCard)\n    }\n\n    \"returns a valid response and track although don't have apps Category, create a moment Category\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.getCurrentCollection returns serviceRight(\n        Option(collection.copy(appsCategory = None)))\n      mockTrackEventProcess.addAppToCollection(any, any) returns serviceRight(Unit)\n      mockSingleCollectionUiActions.addCards(any) returns serviceRight(Unit)\n\n      singleCollectionJobs.addCards(seqCard).mustRightUnit\n\n      there was exactly(seqCard.size)(mockSingleCollectionUiActions).getCurrentCollection\n      there was one(mockSingleCollectionUiActions).addCards(seqCard)\n    }\n\n    \"returns a valid response but not track nothing when don't have appsCategory and moment\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.getCurrentCollection returns serviceRight(\n        Option(collection.copy(appsCategory = None, moment = None)))\n      mockSingleCollectionUiActions.addCards(any) returns serviceRight(Unit)\n\n      singleCollectionJobs.addCards(seqCard).mustRightUnit\n\n      there was exactly(seqCard.size)(mockSingleCollectionUiActions).getCurrentCollection\n      there was one(mockSingleCollectionUiActions).addCards(seqCard)\n    }\n\n    \"Does nothing if cards is Seq.empty\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.addCards(any) returns serviceRight(Unit)\n\n      singleCollectionJobs.addCards(Seq.empty).mustRightUnit\n\n      there was no(mockSingleCollectionUiActions).getCurrentCollection\n      there was one(mockSingleCollectionUiActions).addCards(Seq.empty)\n    }\n\n  }\n  \"removeCards\" should {\n    \"returns a valid response when the service returns a right response\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.getCurrentCollection returns serviceRight(Option(collection))\n      mockTrackEventProcess.removeFromCollection(any, any) returns serviceRight(Unit)\n      mockSingleCollectionUiActions.removeCards(any) returns serviceRight(Unit)\n\n      singleCollectionJobs.removeCards(seqCard).mustRightUnit\n\n      there was exactly(seqCard.size)(mockSingleCollectionUiActions).getCurrentCollection\n      there was exactly(seqCard.size)(mockTrackEventProcess).removeFromCollection(any, any)\n      there was one(mockSingleCollectionUiActions).removeCards(seqCard)\n    }\n\n    \"returns a valid response but not track nothing when don't have current collection\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.getCurrentCollection returns serviceRight(None)\n      mockSingleCollectionUiActions.removeCards(any) returns serviceRight(Unit)\n\n      singleCollectionJobs.removeCards(seqCard).mustRightUnit\n\n      there was exactly(seqCard.size)(mockSingleCollectionUiActions).getCurrentCollection\n      there was one(mockSingleCollectionUiActions).removeCards(seqCard)\n    }\n\n    \"returns a valid response and track although don't have apps Category, create a moment Category\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.getCurrentCollection returns serviceRight(\n        Option(collection.copy(appsCategory = None)))\n      mockTrackEventProcess.removeFromCollection(any, any) returns serviceRight(Unit)\n      mockSingleCollectionUiActions.removeCards(any) returns serviceRight(Unit)\n\n      singleCollectionJobs.removeCards(seqCard).mustRightUnit\n\n      there was exactly(seqCard.size)(mockSingleCollectionUiActions).getCurrentCollection\n      there was one(mockSingleCollectionUiActions).removeCards(seqCard)\n    }\n\n    \"returns a valid response but not track nothing when don't have appsCategory and moment\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.getCurrentCollection returns serviceRight(\n        Option(collection.copy(appsCategory = None, moment = None)))\n      mockSingleCollectionUiActions.removeCards(any) returns serviceRight(Unit)\n\n      singleCollectionJobs.removeCards(seqCard).mustRightUnit\n\n      there was exactly(seqCard.size)(mockSingleCollectionUiActions).getCurrentCollection\n      there was one(mockSingleCollectionUiActions).removeCards(seqCard)\n    }\n\n    \"Does nothing if cards is Seq.empty\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.removeCards(any) returns serviceRight(Unit)\n\n      singleCollectionJobs.removeCards(Seq.empty).mustRightUnit\n\n      there was no(mockSingleCollectionUiActions).getCurrentCollection\n      there was one(mockSingleCollectionUiActions).removeCards(Seq.empty)\n    }\n\n  }\n\n  \"reloadCards\" should {\n    \"calls to action reloadCards\" in new SingleCollectionJobsScope {\n      mockSingleCollectionUiActions.reloadCards(any) returns serviceRight(Unit)\n      singleCollectionJobs.reloadCards(seqCard).mustRightUnit\n      there was one(mockSingleCollectionUiActions).reloadCards(seqCard)\n    }\n  }\n\n  \"bindAnimatedAdapter\" should {\n    \"calls to bindAnimatedAdapter\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.bindAnimatedAdapter(any, any) returns serviceRight(Unit)\n      singleCollectionJobs.bindAnimatedAdapter().mustRightUnit\n      there was one(mockSingleCollectionUiActions).bindAnimatedAdapter(true, collection)\n    }\n\n    \"return a JobException if the service throws an exception\" in new SingleCollectionJobsScope {\n\n      override val singleCollectionJobs =\n        new SingleCollectionJobs(mockAnimateCards, None, mockSingleCollectionUiActions)(\n          contextWrapper) {\n\n          override lazy val di: Injector = mockInjector\n\n        }\n      singleCollectionJobs.bindAnimatedAdapter().mustLeft[JobException]\n      there was no(mockSingleCollectionUiActions).bindAnimatedAdapter(any, any)\n    }\n  }\n\n  \"saveEditedCard\" should {\n    \"Save the edited card\" in new SingleCollectionJobsScope {\n\n      mockCollectionProcess.editCard(any, any, any) returns serviceRight(card)\n      mockSingleCollectionUiActions.reloadCard(any) returns serviceRight(Unit)\n\n      singleCollectionJobs\n        .saveEditedCard(collection.id, card.id, Option(newCardName))\n        .mustRightUnit\n\n      there was one(mockCollectionProcess).editCard(collection.id, card.id, newCardName)\n      there was one(mockSingleCollectionUiActions).reloadCard(card)\n    }\n\n    \"Shows a message form field error if cardName is an empty string\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.showMessageFormFieldError returns serviceRight(Unit)\n\n      singleCollectionJobs.saveEditedCard(collection.id, card.id, Option(\"\")).mustRightUnit\n\n      there was one(mockSingleCollectionUiActions).showMessageFormFieldError\n    }\n\n    \"Shows a message form field error if don't have cardName\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.showMessageFormFieldError returns serviceRight(Unit)\n\n      singleCollectionJobs.saveEditedCard(collection.id, card.id, None).mustRightUnit\n\n      there was one(mockSingleCollectionUiActions).showMessageFormFieldError\n    }\n  }\n\n  \"showData\" should {\n    \"calls to show data\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.showData(any) returns serviceRight(Unit)\n      singleCollectionJobs.showData().mustRightUnit\n      there was one(mockSingleCollectionUiActions).showData(collection.cards.isEmpty)\n    }\n\n    \"return a JobException if the service throws an exception\" in new SingleCollectionJobsScope {\n\n      override val singleCollectionJobs =\n        new SingleCollectionJobs(mockAnimateCards, None, mockSingleCollectionUiActions)(\n          contextWrapper) {\n\n          override lazy val di: Injector = mockInjector\n\n        }\n      singleCollectionJobs.showData().mustLeft[JobException]\n      there was no(mockSingleCollectionUiActions).showData(any)\n    }\n  }\n\n  \"showGenericError\" should {\n    \"calls to action showGenericError\" in new SingleCollectionJobsScope {\n\n      mockSingleCollectionUiActions.showContactUsError() returns serviceRight(Unit)\n      singleCollectionJobs.showGenericError().mustRightUnit\n      there was one(mockSingleCollectionUiActions).showContactUsError()\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/collections/jobs/ToolbarJobsSpecification.scala",
    "content": "package cards.nine.app.ui.collections.jobs\n\nimport cards.nine.app.ui.collections.jobs.uiactions.ToolbarUiActions\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.CollectionValues._\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait ToolbarJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait ToolbarJobsScope extends Scope {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockToolbarUiActions = mock[ToolbarUiActions]\n\n    val toolbarJobs = new ToolbarJobs(mockToolbarUiActions)(contextWrapper)\n\n  }\n\n}\n\nclass ToolbarJobsSpec extends ToolbarJobsSpecification {\n\n  \"pullToClose\" should {\n    \"call to pullCloseScrollY\" in new ToolbarJobsScope {\n\n      mockToolbarUiActions.pullCloseScrollY(any, any) returns serviceRight(Unit)\n      toolbarJobs.pullToClose(scrollY, close = true).mustRightUnit\n      there was one(mockToolbarUiActions).pullCloseScrollY(scrollY, true)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/commons/JobsSpec.scala",
    "content": "package cards.nine.app.ui.commons\n\nimport android.content.Intent.ShortcutIconResource\nimport android.content.{Context, Intent}\nimport android.graphics.Bitmap\nimport android.os.Bundle\nimport cards.nine.app.commons.BroadcastDispatcher._\nimport cards.nine.app.di.Injector\nimport cards.nine.commons._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.{CardTestData, CardValues}\nimport cards.nine.models.{Card, CardData}\nimport cards.nine.models.types.ShortcutCardType\nimport cards.nine.process.collection.CollectionProcess\nimport cards.nine.process.device.DeviceProcess\nimport cards.nine.process.theme.ThemeProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\nclass JobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait JobsScope extends Scope {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockContext = mock[Context]\n\n    contextWrapper.bestAvailable returns mockContext\n\n    implicit val contextSupport = mock[ContextSupport]\n\n    val jobs = new Jobs()(contextWrapper)\n  }\n\n  trait ShortcutJobsScope extends Scope with CardTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    implicit val contextSupport = mock[ContextSupport]\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockCollectionProcess: CollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val mockDeviceProcess: DeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val mockIntent: Intent = mock[Intent]\n\n    val mockBundle: Bundle = mock[Bundle]\n\n    val mockBitmap: Bitmap = mock[Bitmap]\n\n    val mockIconResource: ShortcutIconResource = mock[ShortcutIconResource]\n\n    val shortcutJobs = new ShortcutJobs()(contextWrapper) {\n\n      override implicit lazy val di: Injector = mockInjector\n\n    }\n  }\n\n}\n\nclass JobsSpec extends JobsSpecification {\n\n  \"sendBroadCastTask\" should {\n\n    \"call to send broadcast with the right params\" in new JobsScope {\n\n      val action  = \"myAction\"\n      val command = \"command\"\n\n      jobs.sendBroadCastTask(BroadAction(action, Some(command))).mustRightUnit\n\n      val argMather = beLike[Intent] {\n        case intent =>\n          intent.getAction shouldEqual action\n          intent.getStringExtra(keyType) shouldEqual commandType\n          intent.getStringExtra(keyCommand) shouldEqual command\n      }\n      there was one(mockContext).sendBroadcast(argThat(argMather))\n\n    }\n  }\n\n  \"askBroadCastTask\" should {\n\n    \"call to send broadcast with the right params\" in new JobsScope {\n\n      val action  = \"myAction\"\n      val command = \"command\"\n\n      jobs.askBroadCastTask(BroadAction(action, Some(command))).mustRightUnit\n\n      val argMather = beLike[Intent] {\n        case intent =>\n          intent.getAction shouldEqual action\n          intent.getStringExtra(keyType) shouldEqual questionType\n          intent.getStringExtra(keyCommand) shouldEqual command\n      }\n      there was one(mockContext).sendBroadcast(argThat(argMather))\n\n    }\n  }\n\n}\n\nclass ShortcutJobsSpec extends JobsSpecification {\n\n  \"addNewShortcut\" should {\n\n    \"return None when the intent is null\" in new ShortcutJobsScope {\n      shortcutJobs.addNewShortcut(1, javaNull).mustRight(_ must beNone)\n    }\n\n    \"return None when the intent doesn't have the right extras\" in new ShortcutJobsScope {\n      mockIntent.getExtras returns mockBundle\n      mockBundle.containsKey(any) returns false\n\n      shortcutJobs.addNewShortcut(1, mockIntent).mustRight(_ must beNone)\n    }\n\n    \"return the Card when the intent has the name and intent as extras\" in new ShortcutJobsScope {\n      mockIntent.getExtras returns mockBundle\n      mockBundle.containsKey(Intent.EXTRA_SHORTCUT_NAME) returns true\n      mockBundle.getString(Intent.EXTRA_SHORTCUT_NAME) returns card.term\n      mockBundle.containsKey(Intent.EXTRA_SHORTCUT_INTENT) returns true\n      mockBundle.getParcelable[Intent](Intent.EXTRA_SHORTCUT_INTENT) returns card.intent\n      mockBundle.containsKey(Intent.EXTRA_SHORTCUT_ICON) returns false\n\n      mockCollectionProcess.addCards(any, any) returns TaskService.right(Seq(card))\n\n      shortcutJobs.addNewShortcut(1, mockIntent).mustRight(_ must beSome(card))\n\n      val cardMatcher = beLike[Seq[CardData]] {\n        case seq =>\n          val maybeCard = seq.headOption\n          maybeCard must beSome\n          maybeCard.map(_.term) must beSome(card.term)\n          maybeCard.flatMap(_.packageName) must beNone\n          maybeCard.map(_.cardType) must beSome(ShortcutCardType)\n          maybeCard.map(_.intent) must beSome\n          maybeCard.flatMap(_.imagePath) must beNone\n      }\n      there was one(mockCollectionProcess).addCards(===(1), argThat(cardMatcher))\n    }\n\n    \"return the Card when the intent has the name, the intent and the icon as extras\" in new ShortcutJobsScope {\n      mockIntent.getExtras returns mockBundle\n      mockBundle.containsKey(Intent.EXTRA_SHORTCUT_NAME) returns true\n      mockBundle.getString(Intent.EXTRA_SHORTCUT_NAME) returns card.term\n      mockBundle.containsKey(Intent.EXTRA_SHORTCUT_INTENT) returns true\n      mockBundle.getParcelable[Intent](Intent.EXTRA_SHORTCUT_INTENT) returns card.intent\n      mockBundle.containsKey(Intent.EXTRA_SHORTCUT_ICON) returns true\n      mockBundle.getParcelable[Bitmap](Intent.EXTRA_SHORTCUT_ICON) returns mockBitmap\n\n      mockDeviceProcess.saveShortcutIcon(any, any)(any) returns TaskService.right(\n        CardValues.cardImagePath)\n      mockCollectionProcess.addCards(any, any) returns TaskService.right(Seq(card))\n\n      shortcutJobs.addNewShortcut(1, mockIntent).mustRight(_ must beSome(card))\n\n      val cardMatcher = beLike[Seq[CardData]] {\n        case seq =>\n          val maybeCard = seq.headOption\n          maybeCard must beSome\n          maybeCard.map(_.term) must beSome(card.term)\n          maybeCard.flatMap(_.packageName) must beNone\n          maybeCard.map(_.cardType) must beSome(ShortcutCardType)\n          maybeCard.map(_.intent) must beSome\n          maybeCard.flatMap(_.imagePath) shouldEqual card.imagePath\n      }\n      there was one(mockCollectionProcess).addCards(===(1), argThat(cardMatcher))\n      there was one(mockDeviceProcess).saveShortcutIcon(===(mockBitmap), any)(any)\n    }\n\n    \"return the Card when the intent has the name, the intent and the icon resource as extras\" in new ShortcutJobsScope {\n      mockIntent.getExtras returns mockBundle\n      mockBundle.containsKey(Intent.EXTRA_SHORTCUT_NAME) returns true\n      mockBundle.getString(Intent.EXTRA_SHORTCUT_NAME) returns card.term\n      mockBundle.containsKey(Intent.EXTRA_SHORTCUT_INTENT) returns true\n      mockBundle.getParcelable[Intent](Intent.EXTRA_SHORTCUT_INTENT) returns card.intent\n      mockBundle.containsKey(Intent.EXTRA_SHORTCUT_ICON) returns false\n      mockBundle.containsKey(Intent.EXTRA_SHORTCUT_ICON_RESOURCE) returns true\n      mockBundle.getParcelable[ShortcutIconResource](Intent.EXTRA_SHORTCUT_ICON_RESOURCE) returns mockIconResource\n\n      mockDeviceProcess.decodeShortcutIcon(any)(any) returns TaskService.right(mockBitmap)\n      mockDeviceProcess.saveShortcutIcon(any, any)(any) returns TaskService.right(\n        CardValues.cardImagePath)\n      mockCollectionProcess.addCards(any, any) returns TaskService.right(Seq(card))\n\n      shortcutJobs.addNewShortcut(1, mockIntent).mustRight(_ must beSome(card))\n\n      val cardMatcher = beLike[Seq[CardData]] {\n        case seq =>\n          val maybeCard = seq.headOption\n          maybeCard must beSome\n          maybeCard.map(_.term) must beSome(card.term)\n          maybeCard.flatMap(_.packageName) must beNone\n          maybeCard.map(_.cardType) must beSome(ShortcutCardType)\n          maybeCard.map(_.intent) must beSome\n          maybeCard.flatMap(_.imagePath) shouldEqual card.imagePath\n      }\n      there was one(mockCollectionProcess).addCards(===(1), argThat(cardMatcher))\n      there was one(mockDeviceProcess).saveShortcutIcon(===(mockBitmap), any)(any)\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/commons/dialogs/addMoment/AddMomentJobsSpec.scala",
    "content": "package cards.nine.app.ui.commons.dialogs.addMoment\n\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.BroadAction\nimport cards.nine.app.ui.commons.dialogs.addmoment.{AddMomentJobs, AddMomentUiActions}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.MomentTestData\nimport cards.nine.models.MomentData\nimport cards.nine.models.types._\nimport cards.nine.process.moment.{MomentException, MomentProcess}\nimport cards.nine.process.trackevent.TrackEventProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait AddMomentJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait AddMomentJobsScope extends Scope with MomentTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockAddMomentUiActions = mock[AddMomentUiActions]\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockMomentProcess = mock[MomentProcess]\n\n    mockInjector.momentProcess returns mockMomentProcess\n\n    val addMomentJobs = new AddMomentJobs(mockAddMomentUiActions)(contextWrapper) {\n\n      override lazy val di: Injector = mockInjector\n\n      override def sendBroadCastTask(broadAction: BroadAction) = TaskService.empty\n\n    }\n  }\n\n}\n\nclass AddMomentJobsSpec extends AddMomentJobsSpecification {\n\n  \"initialize\" should {\n    \"return a valid response when the service returns a right response\" in new AddMomentJobsScope {\n\n      mockAddMomentUiActions.initialize() returns serviceRight(Unit)\n      mockAddMomentUiActions.showLoading() returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n      mockAddMomentUiActions.addMoments(any) returns serviceRight(Unit)\n\n      addMomentJobs.initialize().mustRightUnit\n\n      there was one(mockAddMomentUiActions).initialize()\n      there was one(mockAddMomentUiActions).showLoading()\n      there was one(mockMomentProcess).getMoments\n      there was one(mockAddMomentUiActions).addMoments(\n        Seq(StudyMoment, SportMoment, MusicMoment, OutAndAboutMoment, CarMoment))\n    }\n\n    \"returns a MomentException when the service returns an exception\" in new AddMomentJobsScope {\n      mockAddMomentUiActions.initialize() returns serviceRight(Unit)\n\n      mockAddMomentUiActions.showLoading() returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceLeft(MomentException(\"\"))\n\n      addMomentJobs.initialize().mustLeft[MomentException]\n\n      there was one(mockAddMomentUiActions).initialize()\n      there was one(mockAddMomentUiActions).showLoading()\n      there was one(mockMomentProcess).getMoments\n    }\n  }\n\n  \"loadMoments\" should {\n    \"return a valid response when the service returns a right response\" in new AddMomentJobsScope {\n\n      mockAddMomentUiActions.showLoading() returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n      mockAddMomentUiActions.addMoments(any) returns serviceRight(Unit)\n\n      addMomentJobs.loadMoments().mustRightUnit\n\n      there was one(mockAddMomentUiActions).showLoading()\n      there was one(mockMomentProcess).getMoments\n      there was one(mockAddMomentUiActions).addMoments(\n        Seq(StudyMoment, SportMoment, MusicMoment, OutAndAboutMoment, CarMoment))\n    }\n\n    \"shows a empty message in screen when the service returns a right response with all moment possible\" in new AddMomentJobsScope {\n\n      mockAddMomentUiActions.showLoading() returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceRight(\n        Seq(\n          moment(0),\n          moment(1),\n          moment(2),\n          moment(3),\n          moment(4),\n          moment(5),\n          moment(6),\n          moment(7)))\n      mockAddMomentUiActions.showEmptyMessageInScreen() returns serviceRight(Unit)\n\n      addMomentJobs.loadMoments().mustRightUnit\n\n      there was one(mockAddMomentUiActions).showLoading()\n      there was one(mockMomentProcess).getMoments\n      there was one(mockAddMomentUiActions).showEmptyMessageInScreen()\n    }\n\n    \"returns a MomentException when the service returns an exception\" in new AddMomentJobsScope {\n\n      mockAddMomentUiActions.showLoading() returns serviceRight(Unit)\n      mockMomentProcess.getMoments returns serviceLeft(MomentException(\"\"))\n\n      addMomentJobs.loadMoments().mustLeft[MomentException]\n\n      there was one(mockAddMomentUiActions).showLoading()\n      there was one(mockMomentProcess).getMoments\n    }\n  }\n\n  \"addMoment\" should {\n    \"return a valid response when the service returns a right response\" in new AddMomentJobsScope {\n\n      mockTrackEventProcess.addMoment(any) returns serviceRight(Unit)\n      mockMomentProcess.saveMoments(any)(any) returns serviceRight(seqMoment)\n      mockAddMomentUiActions.close() returns serviceRight(Unit)\n\n      addMomentJobs.addMoment(NineCardsMoment.defaultMoment).mustRightUnit\n\n      there was one(mockTrackEventProcess).addMoment(NineCardsMoment.defaultMoment.name)\n      there was one(mockMomentProcess).saveMoments(\n        ===(\n          Seq(\n            MomentData(\n              collectionId = None,\n              timeslot = Seq.empty,\n              wifi = Seq.empty,\n              bluetooth = Seq.empty,\n              headphone = false,\n              momentType = NineCardsMoment.defaultMoment))))(any)\n      there was one(mockAddMomentUiActions).close()\n\n    }\n\n    \"returns a MomentException when the service returns an exception\" in new AddMomentJobsScope {\n\n      mockTrackEventProcess.addMoment(any) returns serviceRight(Unit)\n      mockMomentProcess.saveMoments(any)(any) returns serviceLeft(MomentException(\"\"))\n\n      addMomentJobs.addMoment(NineCardsMoment.defaultMoment).mustLeft[MomentException]\n\n      there was one(mockTrackEventProcess).addMoment(NineCardsMoment.defaultMoment.name)\n      there was one(mockMomentProcess).saveMoments(\n        ===(\n          Seq(\n            MomentData(\n              collectionId = None,\n              timeslot = Seq.empty,\n              wifi = Seq.empty,\n              bluetooth = Seq.empty,\n              headphone = false,\n              momentType = NineCardsMoment.defaultMoment))))(any)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/commons/dialogs/apps/AppsJobsSpec.scala",
    "content": "package cards.nine.app.ui.commons.dialogs.apps\n\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.dialogs.apps.AppsFragment._\nimport cards.nine.app.ui.data.IterableData\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.ApplicationValues._\nimport cards.nine.commons.test.data.{ApiTestData, CardTestData, DeviceTestData}\nimport cards.nine.models.types.GetByName\nimport cards.nine.process.device.DeviceProcess\nimport cards.nine.process.intents.LauncherExecutorProcess\nimport cards.nine.process.recommendations.RecommendationsProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait AppsJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait AppsJobsScope\n      extends Scope\n      with ApiTestData\n      with CardTestData\n      with DeviceTestData\n      with IterableData\n      with Conversions {\n\n    val exception = new Throwable(\"\")\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    implicit val contextSupport = mock[ContextSupport]\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockAppsUiAction = mock[AppsUiActions]\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    val mockRecommendationsProcess = mock[RecommendationsProcess]\n\n    val mockLauncherExecutorProcess = mock[LauncherExecutorProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    mockInjector.recommendationsProcess returns mockRecommendationsProcess\n\n    mockInjector.launcherExecutorProcess returns mockLauncherExecutorProcess\n\n    val appsJobs = new AppsJobs(mockAppsUiAction)(contextWrapper) {\n      override lazy val di: Injector = mockInjector\n    }\n\n  }\n\n}\n\nclass AppsJobsSpec extends AppsJobsSpecification {\n\n  \"initialize\" should {\n    \"call to initialize\" in new AppsJobsScope {\n\n      override val appsJobs = new AppsJobs(mockAppsUiAction)(contextWrapper) {\n        override def loadApps() = TaskService.empty\n      }\n\n      mockAppsUiAction.initialize(cardPackageSet) returns serviceRight(Unit)\n\n      appsJobs.initialize(cardPackageSet).mustRightUnit\n\n      there was one(mockAppsUiAction).initialize(cardPackageSet)\n\n    }\n  }\n\n  \"destroy\" should {\n    \"call to destroy\" in new AppsJobsScope {\n\n      mockAppsUiAction.destroy() returns serviceRight(Unit)\n\n      appsJobs.destroy().mustRightUnit\n\n      there was one(mockAppsUiAction).destroy()\n\n    }\n  }\n\n  \"loadApps\" should {\n    \"return a valid response when the service returns a right response\" in new AppsJobsScope {\n\n      mockAppsUiAction.showLoading() returns serviceRight(Unit)\n      mockAppsUiAction.showSelectedMessageAndFab() returns serviceRight(Unit)\n      mockDeviceProcess.getIterableApps(any)(any) returns serviceRight(iterableCursorApps)\n      mockDeviceProcess.getTermCountersForApps(any)(any) returns serviceRight(appsCounters)\n      mockAppsUiAction.showApps(any, any) returns serviceRight(Unit)\n\n      appsJobs.loadApps().mustRightUnit\n\n      there was one(mockAppsUiAction).showLoading()\n      there was one(mockDeviceProcess).getIterableApps(===(GetByName))(any)\n      there was one(mockDeviceProcess).getTermCountersForApps(===(GetByName))(any)\n      there was one(mockAppsUiAction).showApps(iterableCursorApps, appsCounters)\n\n    }\n\n    \"return a valid response when the service returns no IterableApps \" in new AppsJobsScope {\n\n      mockAppsUiAction.showLoading() returns serviceRight(Unit)\n      mockAppsUiAction.showSelectedMessageAndFab() returns serviceRight(Unit)\n      mockDeviceProcess.getIterableApps(any)(any) returns serviceRight(emptyIterableCursorApps)\n      mockDeviceProcess.getTermCountersForApps(any)(any) returns serviceRight(appsCounters)\n      mockAppsUiAction.showApps(any, any) returns serviceRight(Unit)\n\n      appsJobs.loadApps().mustRightUnit\n\n      there was one(mockAppsUiAction).showLoading()\n      there was one(mockDeviceProcess).getIterableApps(===(GetByName))(any)\n      there was one(mockDeviceProcess).getTermCountersForApps(===(GetByName))(any)\n      there was one(mockAppsUiAction).showApps(emptyIterableCursorApps, appsCounters)\n\n    }\n\n    \"return a valid response when the service returns an empty TermCounters sequence\" in new AppsJobsScope {\n\n      mockAppsUiAction.showLoading() returns serviceRight(Unit)\n      mockAppsUiAction.showSelectedMessageAndFab() returns serviceRight(Unit)\n      mockDeviceProcess.getIterableApps(any)(any) returns serviceRight(iterableCursorApps)\n      mockDeviceProcess.getTermCountersForApps(any)(any) returns serviceRight(Seq.empty)\n      mockAppsUiAction.showApps(any, any) returns serviceRight(Unit)\n\n      appsJobs.loadApps().mustRightUnit\n\n      there was one(mockAppsUiAction).showLoading()\n      there was one(mockDeviceProcess).getIterableApps(===(GetByName))(any)\n      there was one(mockDeviceProcess).getTermCountersForApps(===(GetByName))(any)\n      there was one(mockAppsUiAction).showApps(iterableCursorApps, Seq.empty)\n\n    }\n\n  }\n\n  \"loadSearch\" should {\n    \"return a valid response when the service returns a right response\" in new AppsJobsScope {\n\n      mockAppsUiAction.showLoadingInGooglePlay() returns serviceRight(Unit)\n      mockRecommendationsProcess.searchApps(any, any)(any) returns serviceRight(\n        seqNotCategorizedPackage)\n      mockAppsUiAction.reloadSearch(any) returns serviceRight(Unit)\n\n      appsJobs.loadSearch(keyword).mustRightUnit\n\n      there was one(mockAppsUiAction).showLoadingInGooglePlay()\n      there was one(mockRecommendationsProcess).searchApps(===(keyword), any)(any)\n      there was one(mockAppsUiAction).reloadSearch(seqNotCategorizedPackage)\n\n    }\n\n    \"return a valid response when the service returns an empty sequence\" in new AppsJobsScope {\n\n      mockAppsUiAction.showLoadingInGooglePlay() returns serviceRight(Unit)\n      mockRecommendationsProcess.searchApps(any, any)(any) returns serviceRight(Seq.empty)\n      mockAppsUiAction.reloadSearch(any) returns serviceRight(Unit)\n\n      appsJobs.loadSearch(keyword).mustRightUnit\n\n      there was one(mockAppsUiAction).showLoadingInGooglePlay()\n      there was one(mockRecommendationsProcess).searchApps(===(keyword), any)(any)\n      there was one(mockAppsUiAction).reloadSearch(Seq.empty)\n\n    }\n\n  }\n\n  \"loadAppsByKeyword\" should {\n    \"return a valid response when the service returns a right response\" in new AppsJobsScope {\n\n      mockDeviceProcess.getIterableAppsByKeyWord(any, any)(any) returns serviceRight(\n        iterableCursorApps)\n      mockAppsUiAction.showApps(any, any) returns serviceRight(Unit)\n\n      appsJobs.loadAppsByKeyword(keyword).mustRightUnit\n\n      there was one(mockDeviceProcess).getIterableAppsByKeyWord(===(keyword), ===(GetByName))(any)\n      there was one(mockAppsUiAction).showApps(iterableCursorApps, Seq.empty)\n\n    }\n\n    \"return a valid response when the service returns no IterableApps \" in new AppsJobsScope {\n\n      mockDeviceProcess.getIterableAppsByKeyWord(any, any)(any) returns serviceRight(\n        iterableCursorApps)\n      mockAppsUiAction.showApps(any, any) returns serviceRight(Unit)\n\n      appsJobs.loadAppsByKeyword(keyword).mustRightUnit\n\n      there was one(mockDeviceProcess).getIterableAppsByKeyWord(===(keyword), ===(GetByName))(any)\n      there was one(mockAppsUiAction).showApps(iterableCursorApps, Seq.empty)\n\n    }\n\n  }\n\n  sequential\n  \"getAddedAndRemovedApps\" should {\n    \"return a valid response when no cards are removed or added\" in new AppsJobsScope {\n\n      appStatuses = appStatuses.copy(\n        initialPackages = setApplicationDataPackages,\n        selectedPackages = setApplicationDataPackages)\n\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n\n      appsJobs.getAddedAndRemovedApps.mustRight { result =>\n        val (cardsToAdd, cardsToRemove) = result\n        cardsToAdd shouldEqual Seq.empty\n        cardsToRemove shouldEqual Seq.empty\n      }\n\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n\n    }\n\n    \"return a valid response when some cards are removed and no cards are added\" in new AppsJobsScope {\n\n      appStatuses = appStatuses.copy(\n        initialPackages = setApplicationDataPackages,\n        selectedPackages = setApplicationDataPackages.take(2))\n\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n\n      appsJobs.getAddedAndRemovedApps.mustRight { result =>\n        val (cardsToAdd, cardsToRemove) = result\n        cardsToAdd shouldEqual Seq.empty\n        cardsToRemove shouldEqual Seq(toCardData(seqApplicationData(2)))\n      }\n\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n\n    }\n\n    \"return a valid response when no cards are removed and some cards are added\" in new AppsJobsScope {\n\n      appStatuses = appStatuses.copy(\n        initialPackages = setApplicationDataPackages.take(1),\n        selectedPackages = setApplicationDataPackages)\n\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n\n      val cardsToAddSeq = seqApplicationData.drop(1) map toCardData\n\n      appsJobs.getAddedAndRemovedApps.mustRight { result =>\n        val (cardsToAdd, cardsToRemove) = result\n        cardsToAdd shouldEqual cardsToAddSeq\n        cardsToRemove shouldEqual Seq.empty\n      }\n\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n\n    }\n\n    \"return a valid response when some cards are removed and some cards are added\" in new AppsJobsScope {\n\n      appStatuses = appStatuses.copy(\n        initialPackages = setApplicationDataPackages.take(2),\n        selectedPackages = setApplicationDataPackages.drop(1))\n\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n\n      val cardsToAddSeq    = Seq(toCardData(seqApplicationData(2)))\n      val cardsToRemoveSeq = Seq(toCardData(seqApplicationData(0)))\n\n      appsJobs.getAddedAndRemovedApps.mustRight { result =>\n        val (cardsToAdd, cardsToRemove) = result\n        cardsToAdd shouldEqual cardsToAddSeq\n        cardsToRemove shouldEqual cardsToRemoveSeq\n      }\n\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n\n    }\n  }\n\n  \"updateSelectedApps\" should {\n    \"call to showUpdateSelectedApps\" in new AppsJobsScope {\n\n      mockAppsUiAction.showUpdateSelectedApps(cardPackageSet) returns serviceRight(Unit)\n\n      appsJobs.updateSelectedApps(cardPackageSet).mustRightUnit\n\n      there was one(mockAppsUiAction).showUpdateSelectedApps(cardPackageSet)\n\n    }\n  }\n\n  \"launchGooglePlay\" should {\n    \"call to launchGooglePlay\" in new AppsJobsScope {\n\n      mockLauncherExecutorProcess.launchGooglePlay(any)(any) returns serviceRight(Unit)\n\n      appsJobs.launchGooglePlay(applicationPackageName).mustRightUnit\n\n      there was one(mockLauncherExecutorProcess).launchGooglePlay(===(applicationPackageName))(any)\n\n    }\n  }\n\n  \"showErrorLoadingApps\" should {\n    \"call to showErrorLoadingApps\" in new AppsJobsScope {\n\n      mockAppsUiAction.showErrorLoadingAppsInScreen() returns serviceRight(Unit)\n\n      appsJobs.showErrorLoadingApps().mustRightUnit\n\n      there was one(mockAppsUiAction).showErrorLoadingAppsInScreen()\n\n    }\n  }\n\n  \"showError\" should {\n    \"call to showError\" in new AppsJobsScope {\n\n      mockAppsUiAction.showError() returns serviceRight(Unit)\n\n      appsJobs.showError().mustRightUnit\n\n      there was one(mockAppsUiAction).showError()\n\n    }\n  }\n\n  \"close\" should {\n    \"call to close\" in new AppsJobsScope {\n\n      mockAppsUiAction.close() returns serviceRight(Unit)\n\n      appsJobs.close().mustRightUnit\n\n      there was one(mockAppsUiAction).close()\n\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/commons/dialogs/contacts/ContactsJobsSpec.scala",
    "content": "package cards.nine.app.ui.commons.dialogs.contacts\n\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.RequestCodes\nimport cards.nine.app.ui.data.IterableData\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.DeviceTestData\nimport cards.nine.models.types.AllContacts\nimport cards.nine.process.device.{ContactException, DeviceProcess}\nimport cards.nine.process.trackevent.TrackEventProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\nimport cards.nine.commons.test.data.WizardJobsValues._\nimport cards.nine.commons.test.data.DeviceValues._\n\ntrait ContactsJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait ContactsJobsScope extends Scope with DeviceTestData with IterableData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockContactsUiActions = mock[ContactsUiActions]\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val contactsJobs = new ContactsJobs(mockContactsUiActions)(contextWrapper) {\n\n      override lazy val di: Injector = mockInjector\n\n    }\n  }\n\n}\n\nclass ContactsJobsSpec extends ContactsJobsSpecification {\n\n  \"initialize\" should {\n    \"\" in new ContactsJobsScope {\n\n      mockContactsUiActions.initialize() returns serviceRight(Unit)\n      mockContactsUiActions.showLoading() returns serviceRight(Unit)\n      mockDeviceProcess.getIterableContacts(any)(any) returns serviceRight(iterableContact)\n      mockContactsUiActions.showContacts(any) returns serviceRight(Unit)\n\n      contactsJobs.initialize().mustRightUnit\n\n      there was one(mockContactsUiActions).initialize()\n      there was one(mockContactsUiActions).showLoading()\n    }\n  }\n\n  \"destroy\" should {\n    \"call to destroy\" in new ContactsJobsScope {\n\n      mockContactsUiActions.destroy() returns serviceRight(Unit)\n      contactsJobs.destroy().mustRightUnit\n      there was one(mockContactsUiActions).destroy()\n    }\n  }\n\n  \"loadContacts\" should {\n    \"returns a valid response when the service returns a right response and hasn't a keyword\" in new ContactsJobsScope {\n\n      mockContactsUiActions.showLoading() returns serviceRight(Unit)\n      mockDeviceProcess.getIterableContacts(any)(any) returns serviceRight(iterableContact)\n      mockContactsUiActions.showContacts(any) returns serviceRight(Unit)\n\n      contactsJobs.loadContacts(None).mustRightUnit\n\n      there was one(mockContactsUiActions).showLoading()\n      there was one(mockDeviceProcess).getIterableContacts(===(AllContacts))(any)\n      there was one(mockContactsUiActions).showContacts(any)\n    }\n\n    \"returns a valid response when the service returns a right response and has a keyword\" in new ContactsJobsScope {\n\n      mockContactsUiActions.showLoading() returns serviceRight(Unit)\n      mockDeviceProcess.getIterableContactsByKeyWord(any)(any) returns serviceRight(\n        iterableContact)\n      mockContactsUiActions.showContacts(any) returns serviceRight(Unit)\n\n      contactsJobs.loadContacts(Option(contactKeyword)).mustRightUnit\n\n      there was one(mockContactsUiActions).showLoading()\n      there was one(mockDeviceProcess).getIterableContactsByKeyWord(===(contactKeyword))(any)\n      there was one(mockContactsUiActions).showContacts(any)\n    }\n  }\n\n  \"askForContactsPermission\" should {\n    \"call to askForContactsPermission\" in new ContactsJobsScope {\n\n      mockContactsUiActions.askForContactsPermission(requestCode) returns serviceRight(Unit)\n      contactsJobs.askForContactsPermission(requestCode).mustRightUnit\n      there was one(mockContactsUiActions).askForContactsPermission(requestCode)\n    }\n  }\n\n  \"showContact\" should {\n    \"returns a valid response when the service returns a right response\" in new ContactsJobsScope {\n\n      mockTrackEventProcess.addContactByFab() returns serviceRight(Unit)\n      mockDeviceProcess.getContact(any)(any) returns serviceRight(contact)\n      mockContactsUiActions.showSelectContactDialog(any) returns serviceRight(Unit)\n\n      contactsJobs.showContact(lookupKey).mustRightUnit\n\n      there was one(mockTrackEventProcess).addContactByFab()\n      there was one(mockDeviceProcess).getContact(===(lookupKey))(any)\n      there was one(mockContactsUiActions).showSelectContactDialog(contact)\n    }\n\n    \"returns a ContactException when the service returns an exception\" in new ContactsJobsScope {\n\n      mockTrackEventProcess.addContactByFab() returns serviceRight(Unit)\n      mockDeviceProcess.getContact(any)(any) returns serviceLeft(ContactException(\"\"))\n\n      contactsJobs.showContact(lookupKey).mustLeft[ContactException]\n\n      there was one(mockTrackEventProcess).addContactByFab()\n      there was one(mockDeviceProcess).getContact(===(lookupKey))(any)\n      there was no(mockContactsUiActions).showSelectContactDialog(any)\n    }\n  }\n\n  \"requestPermissionsResult\" should {\n    \"returns a valid response when the service returns a right response\" in new ContactsJobsScope {\n\n      mockContactsUiActions.showLoading() returns serviceRight(Unit)\n      mockDeviceProcess.getIterableContacts(any)(any) returns serviceRight(iterableContact)\n      mockContactsUiActions.showContacts(any) returns serviceRight(Unit)\n\n      contactsJobs\n        .requestPermissionsResult(\n          RequestCodes.contactsPermission,\n          contactPermissions,\n          contactGranResults)\n        .mustRightUnit\n\n      there was one(mockContactsUiActions).showLoading()\n      there was one(mockDeviceProcess).getIterableContacts(===(AllContacts))(any)\n      there was one(mockContactsUiActions).showContacts(any)\n    }\n\n    \"shows a error contacts permission\" in new ContactsJobsScope {\n\n      mockContactsUiActions.showErrorContactsPermission() returns serviceRight(Unit)\n      contactsJobs\n        .requestPermissionsResult(\n          RequestCodes.contactsPermission,\n          contactNoPermissions,\n          contactGranResults)\n        .mustRightUnit\n      there was one(mockContactsUiActions).showErrorContactsPermission()\n    }\n\n    \"Do nothing if requestCode isn't contactsPermission\" in new ContactsJobsScope {\n\n      contactsJobs\n        .requestPermissionsResult(\n          RequestCodes.callLogPermission,\n          contactNoPermissions,\n          contactGranResults)\n        .mustRightUnit\n\n    }\n  }\n\n  \"showError\" should {\n    \"call to showError\" in new ContactsJobsScope {\n\n      mockContactsUiActions.showError() returns serviceRight(Unit)\n      contactsJobs.showError().mustRightUnit\n      there was one(mockContactsUiActions).showError()\n    }\n  }\n\n  \"showErrorLoadingContacts\" should {\n    \"call to showErrorLoadingContactsInScreen\" in new ContactsJobsScope {\n\n      mockContactsUiActions.showErrorLoadingContactsInScreen() returns serviceRight(Unit)\n      contactsJobs.showErrorLoadingContacts().mustRightUnit\n      there was one(mockContactsUiActions).showErrorLoadingContactsInScreen()\n    }\n  }\n\n  \"close\" should {\n    \"call to close\" in new ContactsJobsScope {\n\n      mockContactsUiActions.close() returns serviceRight(Unit)\n      contactsJobs.close().mustRightUnit\n      there was one(mockContactsUiActions).close()\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/commons/dialogs/editmoment/EditMomentJobsSpec.scala",
    "content": "package cards.nine.app.ui.commons.dialogs.editmoment\n\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.{BroadAction, JobException}\nimport cards.nine.app.ui.commons.dialogs.editmoment.EditMomentFragment._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.commons.test.data.MomentValues._\nimport cards.nine.commons.test.data.{CollectionTestData, MomentTestData}\nimport cards.nine.models.types.NineCardsMoment\nimport cards.nine.process.collection.CollectionProcess\nimport cards.nine.process.device.DeviceProcess\nimport cards.nine.process.moment.MomentProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\nimport cards.nine.commons.test.data.CommonValues._\n\ntrait EditMomentJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait EditMomentJobsScope extends Scope with MomentTestData with CollectionTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockEditMomentUiActions = mock[EditMomentUiActions]\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val mockMomentProcess = mock[MomentProcess]\n\n    mockInjector.momentProcess returns mockMomentProcess\n\n    val mockCollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val editMomentJobs = new EditMomentJobs(mockEditMomentUiActions)(contextWrapper) {\n\n      override lazy val di: Injector = mockInjector\n\n      override def sendBroadCastTask(broadAction: BroadAction) = TaskService.empty\n\n    }\n  }\n\n}\n\nclass EditMomentJobsSpec extends EditMomentJobsSpecification {\n\n  sequential\n  \"initialize\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      mockTrackEventProcess.editMoment(any) returns serviceRight(Unit)\n      mockMomentProcess.getMomentByType(any) returns serviceRight(moment)\n      mockCollectionProcess.getCollections returns serviceRight(seqCollection)\n      mockEditMomentUiActions.initialize(any, any) returns serviceRight(Unit)\n\n      editMomentJobs.initialize(NineCardsMoment.defaultMoment).mustRightUnit\n\n      there was one(mockTrackEventProcess).editMoment(NineCardsMoment.defaultMoment.name)\n      there was one(mockMomentProcess).getMomentByType(NineCardsMoment.defaultMoment)\n      there was one(mockCollectionProcess).getCollections\n      there was one(mockEditMomentUiActions).initialize(moment, seqCollection)\n\n    }\n  }\n\n  \"momentNotFound\" should {\n    \"call to close\" in new EditMomentJobsScope {\n\n      mockEditMomentUiActions.close() returns serviceRight(Unit)\n      editMomentJobs.momentNotFound().mustRightUnit\n      there was one(mockEditMomentUiActions).close()\n    }\n  }\n\n  \"setCollectionId\" should {\n    \"returns a valid response when the service returns a right response and modifiedMoment have collectionId equal collectionId\" in new EditMomentJobsScope {\n\n      mockTrackEventProcess.quickAccessToCollection() returns serviceRight(Unit)\n\n      editMomentJobs.setCollectionId(Option(collectionId)).mustRightUnit\n\n      there was one(mockTrackEventProcess).quickAccessToCollection()\n      statuses.modifiedMoment map (_.collectionId shouldEqual Option(collectionId))\n    }\n\n    \"returns a valid response when the service returns a right response and modifiedMoment have collectionId equal None\" in new EditMomentJobsScope {\n\n      mockTrackEventProcess.quickAccessToCollection() returns serviceRight(Unit)\n\n      editMomentJobs.setCollectionId(Option(0)).mustRightUnit\n\n      there was one(mockTrackEventProcess).quickAccessToCollection()\n      statuses.modifiedMoment map (_.collectionId shouldEqual None)\n    }\n  }\n\n  \"swapDay\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      mockEditMomentUiActions.reloadDays(any, any) returns serviceRight(Unit)\n      editMomentJobs.swapDay(position, day).mustRightUnit\n\n    }\n\n    \"returns a JobException when timeslot not found\" in new EditMomentJobsScope {\n\n      editMomentJobs.swapDay(noFoundPosition, day).mustLeft[JobException]\n\n    }\n  }\n\n  \"changeFromHour\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      mockEditMomentUiActions.reloadDays(any, any) returns serviceRight(Unit)\n      editMomentJobs.changeFromHour(position, hour).mustRightUnit\n\n    }\n\n    \"returns a JobException when timeslot not found\" in new EditMomentJobsScope {\n\n      editMomentJobs.changeFromHour(noFoundPosition, hour).mustLeft[JobException]\n\n    }\n  }\n\n  \"changeToHour\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      mockEditMomentUiActions.reloadDays(any, any) returns serviceRight(Unit)\n      editMomentJobs.changeToHour(position, hour).mustRightUnit\n\n    }\n\n    \"returns a JobException when timeslot not found\" in new EditMomentJobsScope {\n\n      editMomentJobs.changeToHour(noFoundPosition, hour).mustLeft[JobException]\n\n    }\n  }\n\n  \"addHour\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = Option(moment))\n      mockTrackEventProcess.setHours() returns serviceRight(Unit)\n      mockEditMomentUiActions.loadHours(any) returns serviceRight(Unit)\n\n      editMomentJobs.addHour().mustRightUnit\n\n      there was one(mockTrackEventProcess).setHours()\n      there was one(mockEditMomentUiActions).loadHours(any)\n    }\n\n    \"shows a message when the item is duplicated\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(\n        modifiedMoment = statuses.modifiedMoment map (_.copy(timeslot = Seq(newTimeslot))))\n\n      mockEditMomentUiActions.showItemDuplicatedMessage() returns serviceRight(Unit)\n      editMomentJobs.addHour().mustRightUnit\n      there was one(mockEditMomentUiActions).showItemDuplicatedMessage()\n    }\n\n    \"shows a error message when the modifiedMoment is None\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = None)\n      mockTrackEventProcess.setHours() returns serviceRight(Unit)\n      mockEditMomentUiActions.showSavingMomentErrorMessage() returns serviceRight(Unit)\n      editMomentJobs.addHour().mustRightUnit\n      there was one(mockEditMomentUiActions).showSavingMomentErrorMessage()\n    }\n  }\n\n  \"removeHour\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = Option(moment))\n      mockEditMomentUiActions.loadHours(any) returns serviceRight(Unit)\n      editMomentJobs.removeHour(position).mustRightUnit\n      there was one(mockEditMomentUiActions).loadHours(any)\n    }\n\n    \"shows a error message when the modifiedMoment is None\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = None)\n      mockEditMomentUiActions.showSavingMomentErrorMessage() returns serviceRight(Unit)\n\n      editMomentJobs.removeHour(position).mustRightUnit\n\n      there was one(mockEditMomentUiActions).showSavingMomentErrorMessage()\n    }\n  }\n\n  \"addWifi\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      mockDeviceProcess.getConfiguredNetworks(any) returns serviceRight(wifiSeq)\n      mockEditMomentUiActions.showWifiDialog(any) returns serviceRight(Unit)\n\n      editMomentJobs.addWifi().mustRightUnit\n\n      there was one(mockDeviceProcess).getConfiguredNetworks(any)\n      there was one(mockEditMomentUiActions).showWifiDialog(wifiSeq)\n    }\n  }\n\n  \"addWifi\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = Option(moment.copy(wifi = Seq.empty)))\n      mockTrackEventProcess.setWifi() returns serviceRight(Unit)\n      mockEditMomentUiActions.loadWifis(any) returns serviceRight(Unit)\n\n      editMomentJobs.addWifi(wifiSeq.head).mustRightUnit\n\n      there was one(mockTrackEventProcess).setWifi()\n      there was one(mockEditMomentUiActions).loadWifis(any)\n    }\n\n    \"shows a message when the item is duplicated\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = Option(moment.copy(wifi = wifiSeq)))\n      mockEditMomentUiActions.showItemDuplicatedMessage() returns serviceRight(Unit)\n      editMomentJobs.addWifi(wifiSeq.head).mustRightUnit\n      there was one(mockEditMomentUiActions).showItemDuplicatedMessage()\n    }\n\n    \"shows a error message when the modifiedMoment is None\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = None)\n      mockTrackEventProcess.setWifi() returns serviceRight(Unit)\n      mockEditMomentUiActions.showSavingMomentErrorMessage() returns serviceRight(Unit)\n      editMomentJobs.addWifi(wifiSeq.head).mustRightUnit\n      there was one(mockEditMomentUiActions).showSavingMomentErrorMessage()\n    }\n  }\n\n  \"removeWifi\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = Option(moment))\n      mockEditMomentUiActions.loadWifis(any) returns serviceRight(Unit)\n      editMomentJobs.removeWifi(position).mustRightUnit\n      there was one(mockEditMomentUiActions).loadWifis(any)\n    }\n\n    \"shows a error message when the modifiedMoment is None\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = None)\n      mockEditMomentUiActions.showSavingMomentErrorMessage() returns serviceRight(Unit)\n      editMomentJobs.removeWifi(position).mustRightUnit\n      there was one(mockEditMomentUiActions).showSavingMomentErrorMessage()\n    }\n  }\n\n  \"addBluetooth\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      mockDeviceProcess.getPairedBluetoothDevices(any) returns serviceRight(bluetoothSeq)\n      mockEditMomentUiActions.showBluetoothDialog(any) returns serviceRight(Unit)\n\n      editMomentJobs.addBluetooth().mustRightUnit\n\n      there was one(mockDeviceProcess).getPairedBluetoothDevices(any)\n      there was one(mockEditMomentUiActions).showBluetoothDialog(bluetoothSeq)\n    }\n  }\n\n  \"addBluetooth\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = Option(moment.copy(bluetooth = Seq.empty)))\n      mockTrackEventProcess.setBluetooth() returns serviceRight(Unit)\n      mockEditMomentUiActions.loadBluetooth(any) returns serviceRight(Unit)\n\n      editMomentJobs.addBluetooth(wifiSeq.head).mustRightUnit\n\n      there was one(mockTrackEventProcess).setBluetooth()\n      there was one(mockEditMomentUiActions).loadBluetooth(any)\n    }\n\n    \"shows a message when the item is duplicated\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = Option(moment.copy(wifi = bluetoothSeq)))\n      mockEditMomentUiActions.showItemDuplicatedMessage() returns serviceRight(Unit)\n      editMomentJobs.addBluetooth(bluetoothSeq.head).mustRightUnit\n      there was one(mockEditMomentUiActions).showItemDuplicatedMessage()\n    }\n\n    \"shows a error message when the modifiedMoment is None\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = None)\n      mockTrackEventProcess.setBluetooth() returns serviceRight(Unit)\n      mockEditMomentUiActions.showSavingMomentErrorMessage() returns serviceRight(Unit)\n      editMomentJobs.addBluetooth(bluetoothSeq.head).mustRightUnit\n      there was one(mockEditMomentUiActions).showSavingMomentErrorMessage()\n    }\n  }\n\n  \"removeBluetooth\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = Option(moment))\n      mockEditMomentUiActions.loadBluetooth(any) returns serviceRight(Unit)\n      editMomentJobs.removeBluetooth(position).mustRightUnit\n      there was one(mockEditMomentUiActions).loadBluetooth(any)\n    }\n\n    \"shows a error message when the modifiedMoment is None\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(modifiedMoment = None)\n      mockEditMomentUiActions.showSavingMomentErrorMessage() returns serviceRight(Unit)\n      editMomentJobs.removeBluetooth(position).mustRightUnit\n      there was one(mockEditMomentUiActions).showSavingMomentErrorMessage()\n    }\n  }\n\n  \"saveMoment\" should {\n    \"returns a valid response when the service returns a right response\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(moment = None, modifiedMoment = Option(moment))\n      mockMomentProcess.updateMoment(any)(any) returns serviceRight(Unit)\n      mockEditMomentUiActions.close() returns serviceRight(Unit)\n\n      editMomentJobs.saveMoment().mustRightUnit\n\n      there was one(mockMomentProcess).updateMoment(any)(any)\n      there was one(mockEditMomentUiActions).close()\n\n    }\n    \"call to close when wasn't modified the moment\" in new EditMomentJobsScope {\n\n      statuses = statuses.copy(moment = None, modifiedMoment = None)\n      mockEditMomentUiActions.close() returns serviceRight(Unit)\n      editMomentJobs.saveMoment().mustRightUnit\n      there was one(mockEditMomentUiActions).close()\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/commons/dialogs/privatecollections/PrivateCollectionsJobsSpec.scala",
    "content": "package cards.nine.app.ui.commons.dialogs.privatecollections\n\nimport cards.nine.app.di.Injector\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.{ApplicationTestData, CollectionTestData}\nimport cards.nine.models.types.GetByName\nimport cards.nine.process.collection.{CollectionException, CollectionProcess}\nimport cards.nine.process.device.DeviceProcess\nimport cards.nine.process.moment.MomentProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait PrivateCollectionsJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait PrivateCollectionsScope extends Scope with CollectionTestData with ApplicationTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockPrivateCollectionsUiActions = mock[PrivateCollectionsUiActions]\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val mockCollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val mockMomentProcess = mock[MomentProcess]\n\n    mockInjector.momentProcess returns mockMomentProcess\n\n    val privateCollectionsJobs =\n      new PrivateCollectionsJobs(mockPrivateCollectionsUiActions)(contextWrapper) {\n\n        override lazy val di: Injector = mockInjector\n\n      }\n  }\n\n}\n\nclass PrivateCollectionsJobsSpec extends PrivateCollectionsJobsSpecification {\n\n  \"initialize\" should {\n    \"returns a valid response when the service returns a right response\" in new PrivateCollectionsScope {\n\n      mockTrackEventProcess.openMyCollections() returns serviceRight(Unit)\n      mockPrivateCollectionsUiActions.initialize() returns serviceRight(Unit)\n      mockPrivateCollectionsUiActions.showLoading() returns serviceRight(Unit)\n      mockCollectionProcess.getCollections returns serviceRight(seqCollection)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n      mockCollectionProcess.generatePrivateCollections(any)(any) returns serviceRight(\n        seqCollectionData)\n      mockPrivateCollectionsUiActions.showEmptyMessageInScreen() returns serviceRight(Unit)\n\n      privateCollectionsJobs.initialize().mustRightUnit\n\n      there was one(mockTrackEventProcess).openMyCollections()\n      there was one(mockPrivateCollectionsUiActions).initialize()\n      there was one(mockPrivateCollectionsUiActions).showLoading()\n      there was one(mockCollectionProcess).getCollections\n      there was one(mockMomentProcess).getMoments\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n      there was one(mockCollectionProcess).generatePrivateCollections(any)(any)\n    }\n  }\n\n  \"loadPrivateCollections\" should {\n    \"returns a valid response when the service returns a right response\" in new PrivateCollectionsScope {\n\n      mockPrivateCollectionsUiActions.showLoading() returns serviceRight(Unit)\n      mockCollectionProcess.getCollections returns serviceRight(seqCollection)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n      mockCollectionProcess.generatePrivateCollections(any)(any) returns serviceRight(\n        seqCollectionData)\n      mockPrivateCollectionsUiActions.showEmptyMessageInScreen() returns serviceRight(Unit)\n\n      privateCollectionsJobs.loadPrivateCollections().mustRightUnit\n\n      there was one(mockPrivateCollectionsUiActions).showLoading()\n      there was one(mockCollectionProcess).getCollections\n      there was one(mockMomentProcess).getMoments\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n      there was one(mockCollectionProcess).generatePrivateCollections(any)(any)\n    }\n\n    \"returns a valid response when the service returns a right response when the newCollections hasn't appsCategory\" in new PrivateCollectionsScope {\n\n      mockPrivateCollectionsUiActions.showLoading() returns serviceRight(Unit)\n      mockCollectionProcess.getCollections returns serviceRight(seqCollection)\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n      mockCollectionProcess.generatePrivateCollections(any)(any) returns serviceRight(\n        seqCollectionData.map(_.copy(appsCategory = None)))\n      mockPrivateCollectionsUiActions.addPrivateCollections(any) returns serviceRight(Unit)\n\n      privateCollectionsJobs.loadPrivateCollections().mustRightUnit\n\n      there was one(mockPrivateCollectionsUiActions).showLoading()\n      there was one(mockCollectionProcess).getCollections\n      there was one(mockMomentProcess).getMoments\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n      there was one(mockCollectionProcess).generatePrivateCollections(any)(any)\n    }\n  }\n\n  \"saveCollection\" should {\n    \"returns a valid response when the service returns a right response\" in new PrivateCollectionsScope {\n\n      mockTrackEventProcess.createNewCollectionFromMyCollection(any) returns serviceRight(Unit)\n      mockCollectionProcess.addCollection(any) returns serviceRight(collection)\n      mockPrivateCollectionsUiActions.close() returns serviceRight(Unit)\n\n      privateCollectionsJobs.saveCollection(collectionData).mustRight(_ shouldEqual collection)\n\n      there was one(mockTrackEventProcess).createNewCollectionFromMyCollection(collectionData.name)\n      there was one(mockCollectionProcess).addCollection(collectionData)\n      there was one(mockPrivateCollectionsUiActions).close()\n    }\n\n    \"returns a CollectionException when the service returns an exception\" in new PrivateCollectionsScope {\n\n      mockTrackEventProcess.createNewCollectionFromMyCollection(any) returns serviceRight(Unit)\n      mockCollectionProcess.addCollection(any) returns serviceLeft(CollectionException(\"\"))\n\n      privateCollectionsJobs.saveCollection(collectionData).mustLeft[CollectionException]\n\n      there was one(mockTrackEventProcess).createNewCollectionFromMyCollection(collectionData.name)\n      there was one(mockCollectionProcess).addCollection(collectionData)\n      there was no(mockPrivateCollectionsUiActions).close()\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/commons/dialogs/publicCollections/PublicCollectionsJobsJobsSpec.scala",
    "content": "package cards.nine.app.ui.commons.dialogs.publicCollections\n\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.dialogs.publicollections.{\n  PublicCollectionsJobs,\n  PublicCollectionsUiActions\n}\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.{ApplicationTestData, SharedCollectionTestData}\nimport cards.nine.models.types._\nimport cards.nine.process.collection.{CollectionException, CollectionProcess}\nimport cards.nine.process.device.{AppException, DeviceProcess}\nimport cards.nine.process.intents.LauncherExecutorProcess\nimport cards.nine.process.sharedcollections.SharedCollectionsProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\nimport cards.nine.app.ui.commons.dialogs.publicollections.PublicCollectionsFragment._\n\ntrait PublicCollectionsJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait PublicCollectionsScope\n      extends Scope\n      with SharedCollectionTestData\n      with ApplicationTestData {\n\n    val exception = new Throwable(\"\")\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockPublicCollectionsUiActions = mock[PublicCollectionsUiActions]\n\n    val mockLauncherExecutorProcess = mock[LauncherExecutorProcess]\n\n    mockInjector.launcherExecutorProcess returns mockLauncherExecutorProcess\n\n    val mockSharedCollectionsProcess = mock[SharedCollectionsProcess]\n\n    mockInjector.sharedCollectionsProcess returns mockSharedCollectionsProcess\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val mockCollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val publicCollectionsJobs =\n      new PublicCollectionsJobs(mockPublicCollectionsUiActions)(contextWrapper) {\n\n        override lazy val di: Injector = mockInjector\n\n        override def getString(res: Int, formatArgs: scala.AnyRef*): String = \"\"\n      }\n\n  }\n\n}\n\nclass PublicCollectionsJobsJobsSpec extends PublicCollectionsJobsSpecification {\n\n  sequential\n  \"initialize\" should {\n    \"returns a valid response when the service returns a right response\" in new PublicCollectionsScope {\n\n      mockTrackEventProcess.openPublicCollections() returns serviceRight(Unit)\n      mockPublicCollectionsUiActions.initialize() returns serviceRight(Unit)\n      mockPublicCollectionsUiActions.showLoading() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.getSharedCollectionsByCategory(any, any, any, any)(any) returns serviceRight(\n        seqSharedCollection)\n      mockPublicCollectionsUiActions.loadPublicCollections(any) returns serviceRight(Unit)\n\n      publicCollectionsJobs.initialize().mustRightUnit\n\n      there was one(mockTrackEventProcess).openPublicCollections()\n      there was one(mockPublicCollectionsUiActions).initialize()\n    }\n\n    \"shows a empty messages when the service returns a Sequence empty\" in new PublicCollectionsScope {\n\n      mockTrackEventProcess.openPublicCollections() returns serviceRight(Unit)\n      mockPublicCollectionsUiActions.initialize() returns serviceRight(Unit)\n      mockPublicCollectionsUiActions.showLoading() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.getSharedCollectionsByCategory(any, any, any, any)(any) returns serviceRight(\n        Seq.empty)\n      mockPublicCollectionsUiActions.showEmptyMessageInScreen() returns serviceRight(Unit)\n\n      publicCollectionsJobs.initialize().mustRightUnit\n\n      there was one(mockTrackEventProcess).openPublicCollections()\n      there was one(mockPublicCollectionsUiActions).initialize()\n    }\n  }\n\n  \"loadPublicCollections\" should {\n    \"returns a valid response and load a public collections when the service returns a right response\" in new PublicCollectionsScope {\n\n      mockPublicCollectionsUiActions.showLoading() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.getSharedCollectionsByCategory(any, any, any, any)(any) returns serviceRight(\n        seqSharedCollection)\n      mockPublicCollectionsUiActions.loadPublicCollections(any) returns serviceRight(Unit)\n\n      publicCollectionsJobs.loadPublicCollections().mustRightUnit\n\n      there was one(mockPublicCollectionsUiActions).showLoading()\n      there was one(mockSharedCollectionsProcess).getSharedCollectionsByCategory(\n        ===(statuses.category),\n        ===(statuses.typeSharedCollection),\n        any,\n        any)(any)\n      there was one(mockPublicCollectionsUiActions).loadPublicCollections(seqSharedCollection)\n    }\n\n    \"shows a empty messages when the service returns a Sequence empty\" in new PublicCollectionsScope {\n\n      mockPublicCollectionsUiActions.showLoading() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.getSharedCollectionsByCategory(any, any, any, any)(any) returns serviceRight(\n        Seq.empty)\n      mockPublicCollectionsUiActions.showEmptyMessageInScreen() returns serviceRight(Unit)\n\n      publicCollectionsJobs.loadPublicCollections().mustRightUnit\n\n      there was one(mockPublicCollectionsUiActions).showLoading()\n      there was one(mockSharedCollectionsProcess).getSharedCollectionsByCategory(\n        ===(statuses.category),\n        ===(statuses.typeSharedCollection),\n        any,\n        any)(any)\n      there was one(mockPublicCollectionsUiActions).showEmptyMessageInScreen()\n    }\n  }\n\n  \"loadPublicCollectionsByCategory\" should {\n    \"returns a valid response and updated category when the service returns a right response\" in new PublicCollectionsScope {\n\n      mockPublicCollectionsUiActions.updateCategory(any) returns serviceRight(Unit)\n      mockPublicCollectionsUiActions.showLoading() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.getSharedCollectionsByCategory(any, any, any, any)(any) returns serviceRight(\n        seqSharedCollection)\n      mockPublicCollectionsUiActions.loadPublicCollections(any) returns serviceRight(Unit)\n\n      publicCollectionsJobs\n        .loadPublicCollectionsByCategory(sharedCollection.category)\n        .mustRightUnit\n\n      there was one(mockPublicCollectionsUiActions).updateCategory(category)\n    }\n\n    \"shows a empty messages when the service returns a Sequence empty\" in new PublicCollectionsScope {\n\n      mockPublicCollectionsUiActions.updateCategory(any) returns serviceRight(Unit)\n      mockPublicCollectionsUiActions.showLoading() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.getSharedCollectionsByCategory(any, any, any, any)(any) returns serviceRight(\n        Seq.empty)\n      mockPublicCollectionsUiActions.showEmptyMessageInScreen() returns serviceRight(Unit)\n\n      publicCollectionsJobs\n        .loadPublicCollectionsByCategory(sharedCollection.category)\n        .mustRightUnit\n\n      there was one(mockPublicCollectionsUiActions).updateCategory(category)\n    }\n  }\n\n  \"loadPublicCollectionsByTypeSharedCollection\" should {\n    \"returns a valid response and updated type category when the service returns a right response\" in new PublicCollectionsScope {\n\n      mockPublicCollectionsUiActions.updateTypeCollection(any) returns serviceRight(Unit)\n      mockPublicCollectionsUiActions.showLoading() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.getSharedCollectionsByCategory(any, any, any, any)(any) returns serviceRight(\n        seqSharedCollection)\n      mockPublicCollectionsUiActions.loadPublicCollections(any) returns serviceRight(Unit)\n\n      publicCollectionsJobs\n        .loadPublicCollectionsByTypeSharedCollection(TopSharedCollection)\n        .mustRightUnit\n\n      there was one(mockPublicCollectionsUiActions).updateTypeCollection(TopSharedCollection)\n    }\n\n    \"shows a empty messages when the service returns a Sequence empty\" in new PublicCollectionsScope {\n\n      mockPublicCollectionsUiActions.updateTypeCollection(any) returns serviceRight(Unit)\n      mockPublicCollectionsUiActions.showLoading() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.getSharedCollectionsByCategory(any, any, any, any)(any) returns serviceRight(\n        Seq.empty)\n      mockPublicCollectionsUiActions.showEmptyMessageInScreen() returns serviceRight(Unit)\n\n      publicCollectionsJobs\n        .loadPublicCollectionsByTypeSharedCollection(TopSharedCollection)\n        .mustRightUnit\n\n      there was one(mockPublicCollectionsUiActions).updateTypeCollection(TopSharedCollection)\n    }\n  }\n\n  \"saveSharedCollection\" should {\n\n    \"returns a valid response and call to subscribe when the service returns a right\" in new PublicCollectionsScope {\n\n      mockSharedCollectionsProcess.updateViewSharedCollection(any)(any) returns serviceRight(Unit)\n      mockTrackEventProcess.createNewCollectionFromPublicCollection(any) returns serviceRight(Unit)\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n      mockCollectionProcess.addCollection(any) returns serviceRight(collection)\n      mockPublicCollectionsUiActions.close() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.subscribe(any)(any) returns serviceRight(Unit)\n\n      publicCollectionsJobs.saveSharedCollection(sharedCollection.copy(\n        publicCollectionStatus = PublishedByOther)) mustRight (_ shouldEqual collection)\n\n      there was one(mockSharedCollectionsProcess).updateViewSharedCollection(\n        ===(sharedCollection.id))(any)\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n      there was one(mockSharedCollectionsProcess).subscribe(\n        ===(sharedCollection.sharedCollectionId))(any)\n    }\n\n    \"doesn't call to subscribe when the status is PublishedByMe\" in new PublicCollectionsScope {\n\n      mockSharedCollectionsProcess.updateViewSharedCollection(any)(any) returns serviceRight(Unit)\n      mockTrackEventProcess.createNewCollectionFromPublicCollection(any) returns serviceRight(Unit)\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n      mockCollectionProcess.addCollection(any) returns serviceRight(collection)\n      mockPublicCollectionsUiActions.close() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.subscribe(any)(any) returns serviceRight(Unit)\n\n      publicCollectionsJobs.saveSharedCollection(sharedCollection.copy(\n        publicCollectionStatus = PublishedByMe)) mustRight (_ shouldEqual collection)\n\n      there was one(mockSharedCollectionsProcess).updateViewSharedCollection(\n        ===(sharedCollection.id))(any)\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n      there was no(mockSharedCollectionsProcess).subscribe(any)(any)\n    }\n\n    \"returns a AppException when the service returns an exception\" in new PublicCollectionsScope {\n\n      mockSharedCollectionsProcess.updateViewSharedCollection(any)(any) returns serviceRight(Unit)\n      mockTrackEventProcess.createNewCollectionFromPublicCollection(any) returns serviceRight(Unit)\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceLeft(AppException(\"\"))\n\n      publicCollectionsJobs.saveSharedCollection(sharedCollection).mustLeft[AppException]\n\n      there was one(mockSharedCollectionsProcess).updateViewSharedCollection(\n        ===(sharedCollection.id))(any)\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n\n    }\n    \"returns a CollectionException when the service returns an exception\" in new PublicCollectionsScope {\n\n      mockSharedCollectionsProcess.updateViewSharedCollection(any)(any) returns serviceRight(Unit)\n      mockTrackEventProcess.createNewCollectionFromPublicCollection(any) returns serviceRight(Unit)\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n      mockCollectionProcess.addCollection(any) returns serviceLeft(CollectionException(\"\"))\n\n      publicCollectionsJobs.saveSharedCollection(sharedCollection).mustLeft[CollectionException]\n\n      there was one(mockSharedCollectionsProcess).updateViewSharedCollection(\n        ===(sharedCollection.id))(any)\n      there was one(mockDeviceProcess).getSavedApps(===(GetByName))(any)\n\n    }\n  }\n\n  \"shareCollection\" should {\n    \"returns a valid response when call to launchShare \" in new PublicCollectionsScope {\n\n      mockLauncherExecutorProcess.launchShare(any)(any) returns serviceRight(Unit)\n      publicCollectionsJobs.shareCollection(sharedCollection).mustRightUnit\n      there was one(mockLauncherExecutorProcess).launchShare(any)(any)\n\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/commons/dialogs/recommendations/RecommendationsJobsSpec.scala",
    "content": "package cards.nine.app.ui.commons.dialogs.recommendations\n\nimport cards.nine.app.di.Injector\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.ApiTestData\nimport cards.nine.models.types.{AllAppsCategory, Photography}\nimport cards.nine.process.intents.{\n  LauncherExecutorProcess,\n  LauncherExecutorProcessPermissionException\n}\nimport cards.nine.process.recommendations.{RecommendationsProcess, RecommendedAppsException}\nimport cards.nine.process.trackevent.TrackEventProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait RecommendationsJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait RecommendationsJobsScope extends Scope with ApiTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockRecommendationsUiActions = mock[RecommendationsUiActions]\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockLauncherExecutorProcess = mock[LauncherExecutorProcess]\n\n    mockInjector.launcherExecutorProcess returns mockLauncherExecutorProcess\n\n    val mockRecommendationsProcess = mock[RecommendationsProcess]\n\n    mockInjector.recommendationsProcess returns mockRecommendationsProcess\n\n    val recommendationsJobs =\n      new RecommendationsJobs(AllAppsCategory, Seq.empty, mockRecommendationsUiActions)(\n        contextWrapper) {\n\n        override lazy val di: Injector = mockInjector\n\n      }\n  }\n\n}\n\nclass RecommendationsJobsSpec extends RecommendationsJobsSpecification {\n\n  \"initialize\" should {\n    \"returns a valid response when the service returns a right response\" in new RecommendationsJobsScope {\n\n      mockRecommendationsUiActions.initialize() returns serviceRight(Unit)\n      mockRecommendationsUiActions.showLoading() returns serviceRight(Unit)\n      mockRecommendationsProcess.getRecommendedAppsByPackages(any, any)(any) returns serviceRight(\n        seqNotCategorizedPackage)\n      mockRecommendationsUiActions.loadRecommendations(any) returns serviceRight(Unit)\n\n      recommendationsJobs.initialize().mustRightUnit\n\n      there was one(mockRecommendationsUiActions).initialize()\n      there was one(mockRecommendationsUiActions).showLoading()\n      there was one(mockRecommendationsProcess)\n        .getRecommendedAppsByPackages(===(Seq.empty), ===(Seq.empty))(any)\n      there was one(mockRecommendationsUiActions).loadRecommendations(seqNotCategorizedPackage)\n    }\n  }\n\n  \"installNow\" should {\n    \"returns a valid response when the service returns a right response\" in new RecommendationsJobsScope {\n\n      mockTrackEventProcess.addRecommendationByFab(any) returns serviceRight(Unit)\n      mockLauncherExecutorProcess.launchGooglePlay(any)(any) returns serviceRight(Unit)\n      mockRecommendationsUiActions.recommendationAdded(any) returns serviceRight(Unit)\n\n      recommendationsJobs.installNow(notCategorizedPackage).mustRightUnit\n\n      there was one(mockTrackEventProcess).addRecommendationByFab(\n        notCategorizedPackage.packageName)\n      there was one(mockLauncherExecutorProcess).launchGooglePlay(\n        ===(notCategorizedPackage.packageName))(any)\n      there was one(mockRecommendationsUiActions).recommendationAdded(notCategorizedPackage)\n    }\n\n    \"returns a LauncherExecutorProcessPermissionException when the service returns an exception\" in new RecommendationsJobsScope {\n\n      mockTrackEventProcess.addRecommendationByFab(any) returns serviceRight(Unit)\n      mockLauncherExecutorProcess.launchGooglePlay(any)(any) returns serviceLeft(\n        LauncherExecutorProcessPermissionException(\"\"))\n\n      recommendationsJobs\n        .installNow(notCategorizedPackage)\n        .mustLeft[LauncherExecutorProcessPermissionException]\n\n      there was one(mockTrackEventProcess).addRecommendationByFab(\n        notCategorizedPackage.packageName)\n      there was one(mockLauncherExecutorProcess).launchGooglePlay(\n        ===(notCategorizedPackage.packageName))(any)\n      there was no(mockRecommendationsUiActions).recommendationAdded(notCategorizedPackage)\n    }\n  }\n\n  \"loadRecommendations\" should {\n    \"returns a valid response when the service returns a right response\" in new RecommendationsJobsScope {\n\n      mockRecommendationsUiActions.showLoading() returns serviceRight(Unit)\n      mockRecommendationsProcess.getRecommendedAppsByPackages(any, any)(any) returns serviceRight(\n        seqNotCategorizedPackage)\n      mockRecommendationsUiActions.loadRecommendations(any) returns serviceRight(Unit)\n\n      recommendationsJobs.loadRecommendations().mustRightUnit\n\n      there was one(mockRecommendationsUiActions).showLoading()\n      there was one(mockRecommendationsProcess)\n        .getRecommendedAppsByPackages(===(Seq.empty), ===(Seq.empty))(any)\n      there was one(mockRecommendationsUiActions).loadRecommendations(seqNotCategorizedPackage)\n    }\n\n    \"returns a RecommendedAppsException when the service returns an exception\" in new RecommendationsJobsScope {\n\n      mockRecommendationsUiActions.showLoading() returns serviceRight(Unit)\n      mockRecommendationsProcess.getRecommendedAppsByPackages(any, any)(any) returns serviceLeft(\n        RecommendedAppsException(\"\"))\n\n      recommendationsJobs.loadRecommendations().mustLeft[RecommendedAppsException]\n\n      there was one(mockRecommendationsUiActions).showLoading()\n      there was one(mockRecommendationsProcess)\n        .getRecommendedAppsByPackages(===(Seq.empty), ===(Seq.empty))(any)\n    }\n\n    \"returns a valid response when the service returns a right response\" in new RecommendationsJobsScope {\n\n      override val recommendationsJobs =\n        new RecommendationsJobs(Photography, Seq.empty, mockRecommendationsUiActions)(\n          contextWrapper) {\n\n          override lazy val di: Injector = mockInjector\n\n        }\n\n      mockRecommendationsUiActions.showLoading() returns serviceRight(Unit)\n      mockRecommendationsProcess.getRecommendedAppsByCategory(any, any)(any) returns serviceRight(\n        seqNotCategorizedPackage)\n      mockRecommendationsUiActions.loadRecommendations(any) returns serviceRight(Unit)\n\n      recommendationsJobs.loadRecommendations().mustRightUnit\n\n      there was one(mockRecommendationsUiActions).showLoading()\n      there was one(mockRecommendationsProcess)\n        .getRecommendedAppsByCategory(===(Photography), ===(Seq.empty))(any)\n      there was one(mockRecommendationsUiActions).loadRecommendations(seqNotCategorizedPackage)\n    }\n  }\n\n  \"showErrorLoadingRecommendation\" should {\n    \"call to showErrorLoadingRecommendationInScreen \" in new RecommendationsJobsScope {\n\n      mockRecommendationsUiActions.showErrorLoadingRecommendationInScreen() returns serviceRight(\n        Unit)\n      recommendationsJobs.showErrorLoadingRecommendation().mustRightUnit\n      there was one(mockRecommendationsUiActions).showErrorLoadingRecommendationInScreen()\n    }\n  }\n  \"showError\" should {\n    \"call to showContactUsError\" in new RecommendationsJobsScope {\n\n      mockRecommendationsUiActions.showContactUsError() returns serviceRight(Unit)\n      recommendationsJobs.showError().mustRightUnit\n      there was one(mockRecommendationsUiActions).showContactUsError()\n    }\n  }\n  \"close\" should {\n    \"call to close\" in new RecommendationsJobsScope {\n\n      mockRecommendationsUiActions.close() returns serviceRight(Unit)\n      recommendationsJobs.close().mustRightUnit\n      there was one(mockRecommendationsUiActions).close()\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/commons/dialogs/shortcuts/ShortcutJobsSpec.scala",
    "content": "package cards.nine.app.ui.commons.dialogs.shortcuts\n\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.ShortcutJobs\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.DeviceTestData\nimport cards.nine.process.device.{DeviceProcess, ShortcutException}\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait ShortcutJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait ShortcutJobsScope extends Scope with DeviceTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockShortcutUiActions = mock[ShortcutDialogUiActions]\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val shortcutJobs = new ShortcutDialogJobs(mockShortcutUiActions)(contextWrapper) {\n\n      override lazy val di: Injector = mockInjector\n\n    }\n\n  }\n\n}\n\nclass ShortcutJobsSpec extends ShortcutJobsSpecification {\n\n  \"initialize\" should {\n    \"returns a valid response when the service returns a right response\" in new ShortcutJobsScope {\n\n      mockShortcutUiActions.initialize() returns serviceRight(Unit)\n      mockShortcutUiActions.showLoading() returns serviceRight(Unit)\n      mockDeviceProcess.getAvailableShortcuts(any) returns serviceRight(seqShortcut)\n      mockShortcutUiActions.loadShortcuts(any) returns serviceRight(Unit)\n\n      shortcutJobs.initialize().mustRightUnit\n\n      there was one(mockShortcutUiActions).initialize()\n      there was one(mockShortcutUiActions).showLoading()\n      there was one(mockDeviceProcess).getAvailableShortcuts(any)\n      there was one(mockShortcutUiActions).loadShortcuts(any)\n    }\n\n    \"returns a ShortcutException when the service returns an exception\" in new ShortcutJobsScope {\n\n      mockShortcutUiActions.initialize() returns serviceRight(Unit)\n      mockShortcutUiActions.showLoading() returns serviceRight(Unit)\n      mockDeviceProcess.getAvailableShortcuts(any) returns serviceLeft(ShortcutException(\"\"))\n\n      shortcutJobs.initialize().mustLeft[ShortcutException]\n\n      there was one(mockShortcutUiActions).initialize()\n      there was one(mockShortcutUiActions).showLoading()\n      there was one(mockDeviceProcess).getAvailableShortcuts(any)\n      there was no(mockShortcutUiActions).loadShortcuts(any)\n    }\n  }\n\n  \"loadShortcuts\" should {\n    \"returns a valid response when the service returns a right response\" in new ShortcutJobsScope {\n\n      mockShortcutUiActions.showLoading() returns serviceRight(Unit)\n      mockDeviceProcess.getAvailableShortcuts(any) returns serviceRight(seqShortcut)\n      mockShortcutUiActions.loadShortcuts(any) returns serviceRight(Unit)\n\n      shortcutJobs.loadShortcuts().mustRightUnit\n\n      there was one(mockShortcutUiActions).showLoading()\n      there was one(mockDeviceProcess).getAvailableShortcuts(any)\n      there was one(mockShortcutUiActions).loadShortcuts(any)\n    }\n\n    \"returns a ShortcutException when the service returns an exception\" in new ShortcutJobsScope {\n\n      mockShortcutUiActions.showLoading() returns serviceRight(Unit)\n      mockDeviceProcess.getAvailableShortcuts(any) returns serviceLeft(ShortcutException(\"\"))\n\n      shortcutJobs.loadShortcuts().mustLeft[ShortcutException]\n\n      there was one(mockShortcutUiActions).showLoading()\n      there was one(mockDeviceProcess).getAvailableShortcuts(any)\n      there was no(mockShortcutUiActions).loadShortcuts(any)\n    }\n  }\n\n  \"configureShortcut\" should {\n    \"call to configureShortcut\" in new ShortcutJobsScope {\n\n      mockShortcutUiActions.close() returns serviceRight(Unit)\n      mockShortcutUiActions.configureShortcut(any) returns serviceRight(Unit)\n\n      shortcutJobs.configureShortcut(shortcut).mustRightUnit\n\n      there was one(mockShortcutUiActions).close()\n      there was one(mockShortcutUiActions).configureShortcut(shortcut)\n    }\n  }\n\n  \"showErrorLoadingShortcuts\" should {\n    \"call to showErrorLoadingShortcutsInScreen\" in new ShortcutJobsScope {\n\n      mockShortcutUiActions.showErrorLoadingShortcutsInScreen() returns serviceRight(Unit)\n      shortcutJobs.showErrorLoadingShortcuts().mustRightUnit\n      there was one(mockShortcutUiActions).showErrorLoadingShortcutsInScreen()\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/commons/dialogs/widgets/WidgetsDialogJobsSpec.scala",
    "content": "package cards.nine.app.ui.commons.dialogs.widgets\n\nimport cards.nine.app.di.Injector\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.AppWidgetTestData\nimport cards.nine.process.device.{DeviceProcess, WidgetException}\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait WidgetsDialogJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait WidgetsDialogJobsScope extends Scope with AppWidgetTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockWidgetsDialogUiActions = mock[WidgetsDialogUiActions]\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val widgetsDialogJobs = new WidgetsDialogJobs(mockWidgetsDialogUiActions) {\n\n      override lazy val di: Injector = mockInjector\n\n    }\n  }\n\n}\n\nclass WidgetsDialogJobsSpec extends WidgetsDialogJobsSpecification {\n\n  \"initialize\" should {\n    \"returns a valid response when the service returns a right response\" in new WidgetsDialogJobsScope {\n\n      mockWidgetsDialogUiActions.initialize() returns serviceRight(Unit)\n      mockWidgetsDialogUiActions.showLoading() returns serviceRight(Unit)\n      mockDeviceProcess.getWidgets(any) returns serviceRight(seqAppsWithWidgets)\n      mockWidgetsDialogUiActions.loadWidgets(any) returns serviceRight(Unit)\n\n      widgetsDialogJobs.initialize().mustRightUnit\n\n      there was one(mockWidgetsDialogUiActions).initialize()\n      there was one(mockWidgetsDialogUiActions).showLoading()\n      there was one(mockDeviceProcess).getWidgets(any)\n      there was one(mockWidgetsDialogUiActions).loadWidgets(seqAppsWithWidgets)\n    }\n  }\n\n  \"loadWidgets\" should {\n    \"returns a valid response when the service returns a right response\" in new WidgetsDialogJobsScope {\n\n      mockWidgetsDialogUiActions.showLoading() returns serviceRight(Unit)\n      mockDeviceProcess.getWidgets(any) returns serviceRight(seqAppsWithWidgets)\n      mockWidgetsDialogUiActions.loadWidgets(any) returns serviceRight(Unit)\n\n      widgetsDialogJobs.loadWidgets().mustRightUnit\n\n      there was one(mockWidgetsDialogUiActions).showLoading()\n      there was one(mockDeviceProcess).getWidgets(any)\n      there was one(mockWidgetsDialogUiActions).loadWidgets(seqAppsWithWidgets)\n    }\n\n    \"returns a WidgetException when the service returns an exception\" in new WidgetsDialogJobsScope {\n\n      mockWidgetsDialogUiActions.showLoading() returns serviceRight(Unit)\n      mockDeviceProcess.getWidgets(any) returns serviceLeft(WidgetException(\"\"))\n      mockWidgetsDialogUiActions.loadWidgets(any) returns serviceRight(Unit)\n\n      widgetsDialogJobs.loadWidgets().mustLeft[WidgetException]\n\n      there was one(mockWidgetsDialogUiActions).showLoading()\n      there was one(mockDeviceProcess).getWidgets(any)\n      there was no(mockWidgetsDialogUiActions).loadWidgets(any)\n    }\n  }\n\n  \"close\" should {\n    \"call to close\" in new WidgetsDialogJobsScope {\n\n      mockWidgetsDialogUiActions.close() returns serviceRight(Unit)\n      widgetsDialogJobs.close().mustRightUnit\n      there was one(mockWidgetsDialogUiActions).close()\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/data/IterableData.scala",
    "content": "package cards.nine.app.ui.data\n\nimport cards.nine.commons._\nimport cards.nine.commons.test.data.{ApplicationTestData, DeviceTestData}\nimport cards.nine.models._\nimport cards.nine.repository.model.{App => RepositoryApp}\nimport cards.nine.services.persistence.conversions.AppConversions\n\ntrait IterableData\n    extends ApplicationTestData\n    with DeviceTestData\n    with AppConversions\n    with NineCardsIntentConversions {\n\n  val mockIterableCursor = new IterableCursor[RepositoryApp] {\n    override def count(): Int = 0\n\n    override def moveToPosition(pos: Int): RepositoryApp = javaNull\n\n    override def close(): Unit = ()\n  }\n\n  val iterableCursorApps = new IterableAppCursor(mockIterableCursor, toApp) {\n    override def count(): Int = seqApplication.length\n\n    override def moveToPosition(pos: Int): ApplicationData = seqApplicationData(pos)\n\n    override def close(): Unit = ()\n  }\n\n  val emptyIterableCursorApps = new IterableAppCursor[RepositoryApp](mockIterableCursor, toApp) {\n    override def count(): Int = 0\n\n    override def moveToPosition(pos: Int): ApplicationData = javaNull\n\n    override def close(): Unit = ()\n  }\n\n  val iterableCursorContact = new IterableCursor[Contact] {\n    override def count(): Int = seqContact.length\n\n    override def moveToPosition(pos: Int): Contact = seqContact(pos)\n\n    override def close(): Unit = ()\n  }\n\n  val iterableContact = new IterableContacts(iterableCursorContact)\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/launcher/jobs/AppDrawerJobsSpecification.scala",
    "content": "package cards.nine.app.ui.launcher.jobs\n\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.RequestCodes\nimport cards.nine.app.ui.data.IterableData\nimport cards.nine.app.ui.launcher.jobs.uiactions.AppDrawerUiActions\nimport cards.nine.app.ui.launcher.types._\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data._\nimport cards.nine.models.types._\nimport cards.nine.process.accounts.UserAccountsProcess\nimport cards.nine.process.device.DeviceProcess\nimport cards.nine.process.recommendations.{\n  RecommendationsProcess,\n  RecommendedAppsConfigurationException,\n  RecommendedAppsException\n}\nimport cards.nine.process.trackevent.TrackEventProcess\nimport cards.nine.services.persistence.PersistenceServiceException\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait AppDrawerJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait AppDrawerJobsScope\n      extends Scope\n      with LauncherTestData\n      with CollectionTestData\n      with ApplicationTestData\n      with DeviceTestData\n      with ApiTestData\n      with IterableData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val exception = new PersistenceServiceException(\"\")\n\n    val mockInjector = mock[Injector]\n\n    val mockAppDrawerUiActions = mock[AppDrawerUiActions]\n\n    val mockUserAccountsProcess = mock[UserAccountsProcess]\n\n    mockInjector.userAccountsProcess returns mockUserAccountsProcess\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val mockRecommendationsProcess = mock[RecommendationsProcess]\n\n    mockInjector.recommendationsProcess returns mockRecommendationsProcess\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val appDrawerJobs = new AppDrawerJobs(mockAppDrawerUiActions)(contextWrapper) {\n\n      override lazy val di: Injector = mockInjector\n    }\n\n  }\n\n}\n\nclass AppDrawerJobsSpec extends AppDrawerJobsSpecification {\n\n  \"loadSearch\" should {\n    \"return a valid response when the service returns a Seq.empty\" in new AppDrawerJobsScope {\n\n      mockTrackEventProcess.goToGooglePlayButton() returns serviceRight(Unit)\n      mockAppDrawerUiActions.showLoadingInGooglePlay() returns serviceRight(Unit)\n      mockRecommendationsProcess.searchApps(any, any)(any) returns serviceRight(Seq.empty)\n      mockAppDrawerUiActions.reloadSearchInDrawer(any) returns serviceRight(Unit)\n\n      appDrawerJobs.loadSearch(querry).mustRightUnit\n\n      there was one(mockTrackEventProcess).goToGooglePlayButton()\n      there was one(mockAppDrawerUiActions).showLoadingInGooglePlay()\n      there was one(mockRecommendationsProcess).searchApps(===(querry), ===(null))(any)\n      there was one(mockAppDrawerUiActions).reloadSearchInDrawer(Seq.empty)\n    }\n\n    \"return a valid response when the service returns a Sequence not categorized\" in new AppDrawerJobsScope {\n\n      mockTrackEventProcess.goToGooglePlayButton() returns serviceRight(Unit)\n      mockAppDrawerUiActions.showLoadingInGooglePlay() returns serviceRight(Unit)\n      mockRecommendationsProcess.searchApps(any, any)(any) returns serviceRight(\n        seqNotCategorizedPackage)\n      mockAppDrawerUiActions.reloadSearchInDrawer(any) returns serviceRight(Unit)\n\n      appDrawerJobs.loadSearch(querry).mustRightUnit\n\n      there was one(mockTrackEventProcess).goToGooglePlayButton()\n      there was one(mockAppDrawerUiActions).showLoadingInGooglePlay()\n      there was one(mockRecommendationsProcess).searchApps(===(querry), ===(null))(any)\n      there was one(mockAppDrawerUiActions).reloadSearchInDrawer(seqNotCategorizedPackage)\n    }\n\n    \"returns a RecommendedAppsException when the service returns a fail response\" in new AppDrawerJobsScope {\n\n      mockTrackEventProcess.goToGooglePlayButton() returns serviceRight(Unit)\n      mockAppDrawerUiActions.showLoadingInGooglePlay() returns serviceRight(Unit)\n      mockRecommendationsProcess.searchApps(any, any)(any) returns serviceLeft(\n        RecommendedAppsException(\"\"))\n\n      appDrawerJobs.loadSearch(querry).mustLeft[RecommendedAppsException]\n\n      there was one(mockTrackEventProcess).goToGooglePlayButton()\n      there was one(mockAppDrawerUiActions).showLoadingInGooglePlay()\n      there was one(mockRecommendationsProcess).searchApps(===(querry), ===(null))(any)\n      there was no(mockAppDrawerUiActions).reloadSearchInDrawer(any)\n    }\n\n    \"returns a RecommendedAppsConfigurationException when the service returns a fail response\" in new AppDrawerJobsScope {\n\n      mockTrackEventProcess.goToGooglePlayButton() returns serviceRight(Unit)\n      mockAppDrawerUiActions.showLoadingInGooglePlay() returns serviceRight(Unit)\n      mockRecommendationsProcess.searchApps(any, any)(any) returns serviceLeft(\n        RecommendedAppsConfigurationException(\"\"))\n\n      appDrawerJobs.loadSearch(querry).mustLeft[RecommendedAppsConfigurationException]\n\n      there was one(mockTrackEventProcess).goToGooglePlayButton()\n      there was one(mockAppDrawerUiActions).showLoadingInGooglePlay()\n      there was one(mockRecommendationsProcess).searchApps(===(querry), ===(null))(any)\n      there was no(mockAppDrawerUiActions).reloadSearchInDrawer(any)\n    }\n  }\n\n  \"loadApps\" should {\n    \"return a valid response when loading the apps with AppsAlphabetical\" in new AppDrawerJobsScope {\n\n      mockTrackEventProcess.goToAppDrawer() returns serviceRight(Unit)\n      mockTrackEventProcess.goToApps() returns serviceRight(Unit)\n      mockTrackEventProcess.goToFiltersByButton(any) returns serviceRight(Unit)\n      mockDeviceProcess.getIterableApps(any)(any) returns serviceRight(iterableCursorApps)\n      mockDeviceProcess.getTermCountersForApps(any)(any) returns serviceRight(appsCounters)\n      mockAppDrawerUiActions.reloadAppsInDrawer(any, any, any) returns serviceRight(Unit)\n\n      appDrawerJobs.loadApps(AppsAlphabetical).mustRightUnit\n\n      there was one(mockTrackEventProcess).goToAppDrawer()\n      there was one(mockTrackEventProcess).goToApps()\n      there was one(mockTrackEventProcess).goToFiltersByButton(GetByName.name)\n      there was one(mockDeviceProcess).getIterableApps(===(GetByName))(any)\n      there was one(mockDeviceProcess).getTermCountersForApps(===(GetByName))(any)\n      there was one(mockAppDrawerUiActions)\n        .reloadAppsInDrawer(iterableCursorApps, GetByName, appsCounters)\n    }\n\n    \"return a valid response when loading the apps with AppsByCategories\" in new AppDrawerJobsScope {\n\n      mockTrackEventProcess.goToAppDrawer() returns serviceRight(Unit)\n      mockTrackEventProcess.goToApps() returns serviceRight(Unit)\n      mockTrackEventProcess.goToFiltersByButton(any) returns serviceRight(Unit)\n      mockDeviceProcess.getIterableApps(any)(any) returns serviceRight(iterableCursorApps)\n      mockDeviceProcess.getTermCountersForApps(any)(any) returns serviceRight(appsCounters)\n      mockAppDrawerUiActions.reloadAppsInDrawer(any, any, any) returns serviceRight(Unit)\n\n      appDrawerJobs.loadApps(AppsByCategories).mustRightUnit\n\n      there was one(mockTrackEventProcess).goToAppDrawer()\n      there was one(mockTrackEventProcess).goToApps()\n      there was one(mockTrackEventProcess).goToFiltersByButton(GetByCategory.name)\n      there was one(mockDeviceProcess).getIterableApps(===(GetByCategory))(any)\n      there was one(mockDeviceProcess).getTermCountersForApps(===(GetByCategory))(any)\n      there was one(mockAppDrawerUiActions)\n        .reloadAppsInDrawer(iterableCursorApps, GetByCategory, appsCounters)\n    }\n\n    \"return a valid response when loading the apps with AppsByLastInstall\" in new AppDrawerJobsScope {\n\n      mockTrackEventProcess.goToAppDrawer() returns serviceRight(Unit)\n      mockTrackEventProcess.goToApps() returns serviceRight(Unit)\n      mockTrackEventProcess.goToFiltersByButton(any) returns serviceRight(Unit)\n      mockDeviceProcess.getIterableApps(any)(any) returns serviceRight(iterableCursorApps)\n      mockDeviceProcess.getTermCountersForApps(any)(any) returns serviceRight(appsCounters)\n      mockAppDrawerUiActions.reloadAppsInDrawer(any, any, any) returns serviceRight(Unit)\n\n      appDrawerJobs.loadApps(AppsByLastInstall).mustRightUnit\n\n      there was one(mockTrackEventProcess).goToAppDrawer()\n      there was one(mockTrackEventProcess).goToApps()\n      there was one(mockTrackEventProcess).goToFiltersByButton(GetByInstallDate.name)\n      there was one(mockDeviceProcess).getIterableApps(===(GetByInstallDate))(any)\n      there was one(mockDeviceProcess).getTermCountersForApps(===(GetByInstallDate))(any)\n      there was one(mockAppDrawerUiActions)\n        .reloadAppsInDrawer(iterableCursorApps, GetByInstallDate, appsCounters)\n    }\n  }\n\n  \"loadContacts\" should {\n    \"return a valid response when loading the contacts with ContactsByLastCall\" in new AppDrawerJobsScope {\n\n      mockTrackEventProcess.goToContacts() returns serviceRight(Unit)\n      mockDeviceProcess.getLastCalls(any) returns serviceRight(seqLastCallsContact)\n      mockAppDrawerUiActions.reloadLastCallContactsInDrawer(any) returns serviceRight(Unit)\n\n      appDrawerJobs.loadContacts(ContactsByLastCall).mustRightUnit\n\n      there was one(mockTrackEventProcess).goToContacts()\n      there was one(mockDeviceProcess).getLastCalls(any)\n      there was one(mockAppDrawerUiActions).reloadLastCallContactsInDrawer(seqLastCallsContact)\n    }\n\n    \"return a valid response when loading the contacts with ContactsFavorites\" in new AppDrawerJobsScope {\n\n      mockTrackEventProcess.goToContacts() returns serviceRight(Unit)\n      mockDeviceProcess.getIterableContacts(any)(any) returns serviceRight(iterableContact)\n      mockDeviceProcess.getTermCountersForContacts(any)(any) returns serviceRight(contactsCounters)\n      mockAppDrawerUiActions.reloadContactsInDrawer(any, any) returns serviceRight(Unit)\n\n      appDrawerJobs.loadContacts(ContactsFavorites).mustRightUnit\n\n      there was one(mockTrackEventProcess).goToContacts()\n      there was one(mockDeviceProcess).getIterableContacts(===(FavoriteContacts))(any)\n      there was one(mockDeviceProcess).getTermCountersForContacts(===(FavoriteContacts))(any)\n      there was one(mockAppDrawerUiActions)\n        .reloadContactsInDrawer(===(iterableContact), ===(contactsCounters))\n    }\n\n    \"return a valid response when loading the contacts with ContactsAlphabetical\" in new AppDrawerJobsScope {\n\n      mockTrackEventProcess.goToContacts() returns serviceRight(Unit)\n      mockDeviceProcess.getIterableContacts(any)(any) returns serviceRight(iterableContact)\n      mockDeviceProcess.getTermCountersForContacts(any)(any) returns serviceRight(contactsCounters)\n      mockAppDrawerUiActions.reloadContactsInDrawer(any, any) returns serviceRight(Unit)\n\n      appDrawerJobs.loadContacts(ContactsAlphabetical).mustRightUnit\n\n      there was one(mockTrackEventProcess).goToContacts()\n      there was one(mockDeviceProcess).getIterableContacts(===(AllContacts))(any)\n      there was one(mockDeviceProcess).getTermCountersForContacts(===(AllContacts))(any)\n      there was one(mockAppDrawerUiActions)\n        .reloadContactsInDrawer(===(iterableContact), ===(contactsCounters))\n    }\n  }\n\n  \"loadAppsByKeyword\" should {\n    \"return a valid response when the service returns a right response\" in new AppDrawerJobsScope {\n\n      mockDeviceProcess.getIterableAppsByKeyWord(any, any)(any) returns serviceRight(\n        iterableCursorApps)\n      mockAppDrawerUiActions.reloadAppsInDrawer(any, any, any) returns serviceRight(Unit)\n\n      appDrawerJobs.loadAppsByKeyword(keyword).mustRightUnit\n\n      there was one(mockDeviceProcess).getIterableAppsByKeyWord(===(keyword), ===(GetByName))(any)\n      there was one(mockAppDrawerUiActions).reloadAppsInDrawer(iterableCursorApps, null, null)\n    }\n\n    \"returns a PersistenceServiceException when the service returns a fail response\" in new AppDrawerJobsScope {\n\n      mockDeviceProcess.getIterableAppsByKeyWord(any, any)(any) returns serviceLeft(exception)\n\n      appDrawerJobs.loadAppsByKeyword(keyword).mustLeft[PersistenceServiceException]\n\n      there was one(mockDeviceProcess).getIterableAppsByKeyWord(===(keyword), ===(GetByName))(any)\n      there was no(mockAppDrawerUiActions).reloadAppsInDrawer(any, any, any)\n    }\n  }\n\n  \"loadContactsByKeyword\" should {\n    \"return a valid response when the service returns a right response\" in new AppDrawerJobsScope {\n\n      mockDeviceProcess.getIterableContactsByKeyWord(any)(any) returns serviceRight(\n        iterableContact)\n      mockAppDrawerUiActions.reloadContactsInDrawer(any, any) returns serviceRight(Unit)\n\n      appDrawerJobs.loadContactsByKeyword(keyword).mustRightUnit\n\n      there was one(mockDeviceProcess).getIterableContactsByKeyWord(===(keyword))(any)\n      there was one(mockAppDrawerUiActions).reloadContactsInDrawer(iterableContact, null)\n    }\n\n    \"returns a PersistenceServiceException when the service returns a fail response\" in new AppDrawerJobsScope {\n\n      mockDeviceProcess.getIterableContactsByKeyWord(any)(any) returns serviceLeft(exception)\n\n      appDrawerJobs.loadContactsByKeyword(keyword).mustLeft[PersistenceServiceException]\n\n      there was one(mockDeviceProcess).getIterableContactsByKeyWord(===(keyword))(any)\n      there was no(mockAppDrawerUiActions).reloadContactsInDrawer(any, any)\n    }\n  }\n\n  \"requestReadContacts\" should {\n    \"return a valid response when the service returns a right response\" in new AppDrawerJobsScope {\n\n      mockUserAccountsProcess.requestPermission(any, any)(any) returns serviceRight(Unit)\n      appDrawerJobs.requestReadContacts().mustRightUnit\n      there was one(mockUserAccountsProcess)\n        .requestPermission(===(RequestCodes.contactsPermission), ===(ReadContacts))(any)\n    }\n  }\n\n  \"requestReadCallLog\" should {\n    \"return a valid response when the service returns a right response\" in new AppDrawerJobsScope {\n\n      mockUserAccountsProcess.requestPermission(any, any)(any) returns serviceRight(Unit)\n      appDrawerJobs.requestReadCallLog().mustRightUnit\n      there was one(mockUserAccountsProcess)\n        .requestPermission(===(RequestCodes.callLogPermission), ===(ReadCallLog))(any)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/launcher/jobs/DragJobsSpecification.scala",
    "content": "package cards.nine.app.ui.launcher.jobs\n\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.{BroadAction, JobException}\nimport cards.nine.app.ui.launcher.{AddItemMode, NormalMode, ReorderMode}\nimport cards.nine.app.ui.launcher.jobs.uiactions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.{CollectionTestData, DockAppTestData}\nimport cards.nine.models.DockAppData\nimport cards.nine.models.types._\nimport cards.nine.process.collection.CollectionProcess\nimport cards.nine.process.device.DeviceProcess\nimport cards.nine.process.intents.LauncherExecutorProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\nimport cards.nine.app.ui.launcher.LauncherActivity._\n\ntrait DragJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait DragJobsScope\n      extends Scope\n      with LauncherTestData\n      with DockAppTestData\n      with CollectionTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector = mock[Injector]\n\n    val mockLauncherDOM = mock[LauncherDOM]\n\n    val mockAppDrawerUiActions = mock[AppDrawerUiActions]\n\n    mockAppDrawerUiActions.dom returns mockLauncherDOM\n\n    val mockNavigationUiActions = mock[NavigationUiActions]\n\n    val mockDockAppsUiActions = mock[DockAppsUiActions]\n\n    val mockWorkspaceUiActions = mock[WorkspaceUiActions]\n\n    val mockDragUiActions = mock[DragUiActions]\n\n    mockDragUiActions.dom returns mockLauncherDOM\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val mockCollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val mockLauncherExecutorProcess = mock[LauncherExecutorProcess]\n\n    mockInjector.launcherExecutorProcess returns mockLauncherExecutorProcess\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val dragJobs = new DragJobs(\n      mockAppDrawerUiActions,\n      mockNavigationUiActions,\n      mockDockAppsUiActions,\n      mockWorkspaceUiActions,\n      mockDragUiActions)(contextWrapper) {\n\n      override lazy val di: Injector = mockInjector\n\n      override def sendBroadCastTask(broadAction: BroadAction) = TaskService.empty\n\n    }\n  }\n\n}\n\nclass DragJobsSpec extends DragJobsSpecification {\n\n  sequential\n  \"startAddItemToCollection\" should {\n\n    \"Added an Application to Collection when is collection in workspace\" in new DragJobsScope {\n\n      mockTrackEventProcess.addAppToCollection(any) returns serviceRight(Unit)\n      mockAppDrawerUiActions.close() returns serviceRight(Unit)\n      mockLauncherDOM.isCollectionWorkspace returns true\n      mockDragUiActions.startAddItem(any) returns serviceRight(Unit)\n\n      dragJobs.startAddItemToCollection(applicationData).mustRightUnit\n\n      there was one(mockTrackEventProcess).addAppToCollection(applicationData.packageName)\n      there was one(mockAppDrawerUiActions).close()\n      there was one(mockDragUiActions).startAddItem(AppCardType)\n      there was one(mockNavigationUiActions).goToCollectionWorkspace()\n    }\n\n    \"Added an Application to Collection when isn't collection in workspace\" in new DragJobsScope {\n\n      mockTrackEventProcess.addAppToCollection(any) returns serviceRight(Unit)\n      mockAppDrawerUiActions.close() returns serviceRight(Unit)\n      mockLauncherDOM.isCollectionWorkspace returns false\n      mockNavigationUiActions.goToCollectionWorkspace() returns serviceRight(Unit)\n      mockDragUiActions.startAddItem(any) returns serviceRight(Unit)\n\n      dragJobs.startAddItemToCollection(applicationData).mustRightUnit\n\n      there was one(mockTrackEventProcess).addAppToCollection(applicationData.packageName)\n      there was one(mockAppDrawerUiActions).close()\n      there was one(mockDragUiActions).startAddItem(AppCardType)\n      there was one(mockNavigationUiActions).goToCollectionWorkspace()\n    }\n  }\n\n  \"startAddItemToCollection\" should {\n    \"Added a Contact to Collection when is collection in workspace\" in new DragJobsScope {\n\n      mockTrackEventProcess.addContactToCollection() returns serviceRight(Unit)\n      mockAppDrawerUiActions.close() returns serviceRight(Unit)\n      mockLauncherDOM.isCollectionWorkspace returns true\n      mockDragUiActions.startAddItem(any) returns serviceRight(Unit)\n\n      dragJobs.startAddItemToCollection(contact).mustRightUnit\n\n      there was one(mockTrackEventProcess).addContactToCollection()\n      there was one(mockAppDrawerUiActions).close()\n      there was one(mockDragUiActions).startAddItem(ContactCardType)\n      there was one(mockNavigationUiActions).goToCollectionWorkspace()\n    }\n\n    \"Added an Contact to Collection when isn't collection in workspace\" in new DragJobsScope {\n\n      mockTrackEventProcess.addContactToCollection() returns serviceRight(Unit)\n      mockAppDrawerUiActions.close() returns serviceRight(Unit)\n      mockLauncherDOM.isCollectionWorkspace returns false\n      mockNavigationUiActions.goToCollectionWorkspace() returns serviceRight(Unit)\n      mockDragUiActions.startAddItem(any) returns serviceRight(Unit)\n\n      dragJobs.startAddItemToCollection(contact).mustRightUnit\n\n      there was one(mockTrackEventProcess).addContactToCollection()\n      there was one(mockAppDrawerUiActions).close()\n      there was one(mockDragUiActions).startAddItem(ContactCardType)\n      there was one(mockNavigationUiActions).goToCollectionWorkspace()\n    }\n  }\n\n  \"startAddItemToCollection\" should {\n    \"Added a DockApp with DockType equal AppDockType to Collection\" in new DragJobsScope {\n\n      mockDeviceProcess.deleteDockAppByPosition(any) returns serviceRight(Unit)\n      mockLauncherDOM.isCollectionWorkspace returns true\n      mockDragUiActions.startAddItemFromDockApp(any) returns serviceRight(Unit)\n\n      dragJobs.startAddItemToCollection(dockAppData).mustRightUnit\n\n      there was one(mockDeviceProcess).deleteDockAppByPosition(dockAppData.position)\n      there was one(mockDragUiActions).startAddItemFromDockApp(AppCardType)\n      there was one(mockNavigationUiActions).goToCollectionWorkspace()\n    }\n\n    \"Added a DockApp with DockType equal ContactDockType to Collection\" in new DragJobsScope {\n\n      mockDeviceProcess.deleteDockAppByPosition(any) returns serviceRight(Unit)\n      mockLauncherDOM.isCollectionWorkspace returns true\n      mockDragUiActions.startAddItemFromDockApp(any) returns serviceRight(Unit)\n\n      dragJobs.startAddItemToCollection(dockAppData.copy(dockType = ContactDockType)).mustRightUnit\n\n      there was one(mockDeviceProcess).deleteDockAppByPosition(dockAppData.position)\n      there was one(mockDragUiActions).startAddItemFromDockApp(ContactCardType)\n      there was one(mockNavigationUiActions).goToCollectionWorkspace()\n    }\n\n    \"returns a JobException when added a DockApp with DockType equal CollectionDockType to Collection\" in new DragJobsScope {\n\n      dragJobs\n        .startAddItemToCollection(dockAppData.copy(dockType = CollectionDockType))\n        .mustLeft[JobException]\n\n      there was no(mockDeviceProcess).deleteDockAppByPosition(dockAppData.position)\n      there was no(mockDragUiActions).startAddItemFromDockApp(ContactCardType)\n      there was no(mockNavigationUiActions).goToCollectionWorkspace()\n    }\n  }\n\n  \"draggingAddItemTo\" should {\n    \"Update the position in statuses\" in new DragJobsScope {\n\n      dragJobs.draggingAddItemTo(position).mustRightUnit\n      statuses.currentDraggingPosition shouldEqual position\n    }\n  }\n\n  \"draggingAddItemToPreviousScreen\" should {\n    \"call goToPreviousScreenAddingItem and update current position in statuses\" in new DragJobsScope {\n\n      mockDragUiActions.goToPreviousScreenAddingItem() returns serviceRight(Unit)\n      dragJobs.draggingAddItemToPreviousScreen(position).mustRightUnit\n      there was one(mockDragUiActions).goToPreviousScreenAddingItem()\n    }\n  }\n\n  \"draggingAddItemToNextScreen\" should {\n    \"call goToNextScreenAddingItem and update current position in statuses\" in new DragJobsScope {\n\n      mockDragUiActions.goToNextScreenAddingItem() returns serviceRight(Unit)\n      dragJobs.draggingAddItemToNextScreen(position).mustRightUnit\n      there was one(mockDragUiActions).goToNextScreenAddingItem()\n    }\n  }\n  sequential\n  \"endAddItemToCollection\" should {\n    \"call addCard when has card and collection\" in new DragJobsScope {\n\n      statuses = statuses.copy(cardAddItemMode = Option(cardData))\n      mockLauncherDOM.getCollection(any) returns Option(collection)\n      mockCollectionProcess.addCards(any, any) returns serviceRight(seqCard)\n      mockNavigationUiActions.showAddItemMessage(any) returns serviceRight(Unit)\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.endAddItemToCollection().mustRightUnit\n\n      there was one(mockCollectionProcess).addCards(collection.id, Seq(cardData))\n      there was one(mockNavigationUiActions).showAddItemMessage(collection.name)\n      there was one(mockDragUiActions).endAddItem()\n    }\n\n    \"Does nothing if hasn't card and collection\" in new DragJobsScope {\n\n      statuses = statuses.copy(cardAddItemMode = None)\n      mockLauncherDOM.getCollection(any) returns None\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.endAddItemToCollection().mustRightUnit\n\n      there was one(mockDragUiActions).endAddItem()\n    }\n  }\n  \"changePositionDockApp\" should {\n    \"call to createOrUpdateDockApp when found a dockApps with from position\" in new DragJobsScope {\n\n      mockDeviceProcess.getDockApps returns serviceRight(seqDockApp)\n      mockDeviceProcess.createOrUpdateDockApp(any, any, any, any, any) returns serviceRight(Unit)\n      mockDockAppsUiActions.reloadDockApps(any) returns serviceRight(Unit)\n\n      dragJobs.changePositionDockApp(positionFrom, positionTo).mustRightUnit\n      there was one(mockDeviceProcess).getDockApps\n      there was one(mockDeviceProcess).createOrUpdateDockApp(\n        dockApp.name,\n        dockApp.dockType,\n        dockApp.intent,\n        dockApp.imagePath,\n        positionTo)\n      there was one(mockDockAppsUiActions).reloadDockApps(\n        dockApp.toData.copy(position = positionTo))\n    }\n\n    \"returns an Unit when not found the position\" in new DragJobsScope {\n\n      mockDeviceProcess.getDockApps returns serviceRight(seqDockApp)\n      dragJobs.changePositionDockApp(positionFromNoExist, positionTo).mustRightUnit\n      there was one(mockDeviceProcess).getDockApps\n    }\n  }\n\n  sequential\n  \"endAddItemToDockApp\" should {\n    \"call to createORUpdateDockApp when statuses has a cardAddItemMode, this case AppCardType\" in new DragJobsScope {\n\n      statuses = statuses.copy(cardAddItemMode = Option(cardData.copy(cardType = AppCardType)))\n      mockDeviceProcess.createOrUpdateDockApp(any, any, any, any, any) returns serviceRight(Unit)\n      mockDockAppsUiActions.reloadDockApps(any) returns serviceRight(Unit)\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.endAddItemToDockApp(position).mustRightUnit\n\n      there was one(mockDeviceProcess).createOrUpdateDockApp(\n        cardData.term,\n        AppDockType,\n        cardData.intent,\n        cardData.imagePath getOrElse \"\",\n        position)\n      there was one(mockDockAppsUiActions).reloadDockApps(\n        DockAppData(\n          cardData.term,\n          AppDockType,\n          cardData.intent,\n          cardData.imagePath getOrElse \"\",\n          position))\n      there was one(mockDragUiActions).endAddItem()\n    }\n\n    \"call to createORUpdateDockApp when statuses has a cardAddItemMode, this case AppCardType\" in new DragJobsScope {\n\n      statuses = statuses.copy(cardAddItemMode = Option(cardData.copy(cardType = ContactCardType)))\n      mockDeviceProcess.createOrUpdateDockApp(any, any, any, any, any) returns serviceRight(Unit)\n      mockDockAppsUiActions.reloadDockApps(any) returns serviceRight(Unit)\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.endAddItemToDockApp(position).mustRightUnit\n\n      there was one(mockDeviceProcess).createOrUpdateDockApp(\n        cardData.term,\n        ContactDockType,\n        cardData.intent,\n        cardData.imagePath getOrElse \"\",\n        position)\n      there was one(mockDockAppsUiActions).reloadDockApps(\n        DockAppData(\n          cardData.term,\n          ContactDockType,\n          cardData.intent,\n          cardData.imagePath getOrElse \"\",\n          position))\n      there was one(mockDragUiActions).endAddItem()\n    }\n\n    \"show a message error if has a cardAddItemMode but it's different the AppCardType or ContactCardType\" in new DragJobsScope {\n\n      statuses = statuses.copy(cardAddItemMode = Option(cardData.copy(cardType = EmailCardType)))\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.endAddItemToDockApp(position).mustRightUnit\n\n      there was one(mockNavigationUiActions).showContactUsError()\n    }\n\n    \"show a message error if hasn't a cardAddItemMode\" in new DragJobsScope {\n\n      statuses = statuses.copy(cardAddItemMode = None)\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.endAddItemToDockApp(position).mustRightUnit\n\n      there was one(mockNavigationUiActions).showContactUsError()\n    }\n  }\n\n  sequential\n  \"endAddItem\" should {\n    \"call to endAddItem if statuses is AddItemMode\" in new DragJobsScope {\n\n      statuses = statuses.copy(mode = AddItemMode)\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.endAddItem().mustRightUnit\n\n      there was one(mockDragUiActions).endAddItem()\n    }\n\n    \"Does nothing if statuses is different the AddItemMode\" in new DragJobsScope {\n\n      statuses = statuses.copy(mode = NormalMode)\n      dragJobs.endAddItem().mustRightUnit\n      there was no(mockDragUiActions).endAddItem()\n    }\n  }\n\n  sequential\n  \"uninstallInAddItem\" should {\n    \"call to launchUninstall when statuses has carData and cardType is equal AppCardType and has a packagename\" in new DragJobsScope {\n\n      statuses = statuses.copy(cardAddItemMode = Option(cardData.copy(cardType = AppCardType)))\n      mockLauncherExecutorProcess.launchUninstall(any)(any) returns serviceRight(Unit)\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.uninstallInAddItem().mustRightUnit\n\n      there was one(mockLauncherExecutorProcess).launchUninstall(\n        ===(cardData.packageName.getOrElse(\"\")))(any)\n      there was one(mockDragUiActions).endAddItem()\n    }\n\n    \"Does nothing when statuses has carData and cardType is equal AppCardType and hasn't a packagename\" in new DragJobsScope {\n\n      statuses = statuses.copy(\n        cardAddItemMode = Option(cardData.copy(cardType = AppCardType, packageName = None)))\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.uninstallInAddItem().mustRightUnit\n\n      there was no(mockLauncherExecutorProcess).launchUninstall(any)(any)\n      there was one(mockDragUiActions).endAddItem()\n    }\n\n    \"Does nothing when statuses has carData and cardType is different to AppCardType \" in new DragJobsScope {\n\n      statuses = statuses.copy(cardAddItemMode = Option(cardData.copy(cardType = SmsCardType)))\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.uninstallInAddItem().mustRightUnit\n\n      there was no(mockLauncherExecutorProcess).launchUninstall(any)(any)\n      there was one(mockDragUiActions).endAddItem()\n    }\n\n    \"Does nothing when statuses hasn't carData \" in new DragJobsScope {\n\n      statuses = statuses.copy(cardAddItemMode = None)\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.uninstallInAddItem().mustRightUnit\n\n      there was no(mockLauncherExecutorProcess).launchUninstall(any)(any)\n      there was one(mockDragUiActions).endAddItem()\n    }\n\n  }\n\n  sequential\n  \"settingsInAddItem\" should {\n    \"call to launchSettings when statuses has carData and cardType is equal AppCardType and has a packagename\" in new DragJobsScope {\n\n      statuses = statuses.copy(cardAddItemMode = Option(cardData.copy(cardType = AppCardType)))\n      mockLauncherExecutorProcess.launchSettings(any)(any) returns serviceRight(Unit)\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.settingsInAddItem().mustRightUnit\n\n      there was one(mockLauncherExecutorProcess).launchSettings(\n        ===(cardData.packageName.getOrElse(\"\")))(any)\n      there was one(mockDragUiActions).endAddItem()\n    }\n\n    \"Does nothing when statuses has carData and cardType is equal AppCardType and hasn't a packagename\" in new DragJobsScope {\n\n      statuses = statuses.copy(\n        cardAddItemMode = Option(cardData.copy(cardType = AppCardType, packageName = None)))\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.settingsInAddItem().mustRightUnit\n\n      there was no(mockLauncherExecutorProcess).launchSettings(any)(any)\n      there was one(mockDragUiActions).endAddItem()\n    }\n\n    \"Does nothing when statuses has carData and cardType is different to AppCardType \" in new DragJobsScope {\n\n      statuses = statuses.copy(cardAddItemMode = Option(cardData.copy(cardType = SmsCardType)))\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.settingsInAddItem().mustRightUnit\n\n      there was no(mockLauncherExecutorProcess).launchSettings(any)(any)\n      there was one(mockDragUiActions).endAddItem()\n    }\n\n    \"Does nothing when statuses hasn't carData \" in new DragJobsScope {\n\n      statuses = statuses.copy(cardAddItemMode = None)\n      mockDragUiActions.endAddItem() returns serviceRight(Unit)\n\n      dragJobs.settingsInAddItem().mustRightUnit\n\n      there was no(mockLauncherExecutorProcess).launchSettings(any)(any)\n      there was one(mockDragUiActions).endAddItem()\n    }\n  }\n  sequential\n  \"startReorder\" should {\n    \"update statuses if has a Collection \" in new DragJobsScope {\n\n      mockDragUiActions.startReorder() returns serviceRight(Unit)\n\n      dragJobs.startReorder(Option(collection), position).mustRightUnit\n\n      there was one(mockDragUiActions).startReorder()\n      statuses.startPositionReorderMode equals position\n      statuses.currentDraggingPosition equals position\n      statuses.collectionReorderMode equals Some(collection)\n      statuses.mode equals ReorderMode\n    }\n\n    \"show a message error if hasn't a Collection \" in new DragJobsScope {\n\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n\n      dragJobs.startReorder(None, position).mustRightUnit\n\n      there was no(mockDragUiActions).startReorder()\n      there was one(mockNavigationUiActions).showContactUsError()\n    }\n  }\n\n  sequential\n  \"draggingReorderTo\" should {\n    \"update current position in statuses\" in new DragJobsScope {\n\n      dragJobs.draggingReorderTo(position).mustRightUnit\n      statuses.currentDraggingPosition equals position\n    }\n  }\n\n  sequential\n  \"draggingReorderToNextScreen\" should {\n    \"update current position in statuses\" in new DragJobsScope {\n\n      mockDragUiActions.goToNextScreenReordering() returns serviceRight(Unit)\n\n      dragJobs.draggingReorderToNextScreen(position).mustRightUnit\n\n      there was one(mockDragUiActions).goToNextScreenReordering()\n      statuses.currentDraggingPosition equals position\n    }\n  }\n\n  sequential\n  \"draggingReorderToPreviousScreen\" should {\n    \"update current position in statuses\" in new DragJobsScope {\n\n      mockDragUiActions.goToPreviousScreenReordering() returns serviceRight(Unit)\n\n      dragJobs.draggingReorderToPreviousScreen(position).mustRightUnit\n\n      there was one(mockDragUiActions).goToPreviousScreenReordering()\n      statuses.currentDraggingPosition equals position\n    }\n  }\n\n  sequential\n  \"dropReorder\" should {\n    \"call reorderCollection if startPositionReorderMode is different to currentDraggingPosition and statuses.mode is ReorderMode\" in new DragJobsScope {\n\n      statuses = statuses.copy(\n        mode = ReorderMode,\n        startPositionReorderMode = positionFrom,\n        currentDraggingPosition = positionTo)\n      mockTrackEventProcess.reorderCollection() returns serviceRight(Unit)\n      mockDragUiActions.endReorder() returns serviceRight(Unit)\n      mockCollectionProcess.reorderCollection(any, any) returns serviceRight(Unit)\n      mockWorkspaceUiActions.reloadWorkspaces(any, any) returns serviceRight(Unit)\n      mockLauncherDOM.getData returns seqLauncherData\n\n      dragJobs.dropReorder().mustRightUnit\n\n      there was one(mockTrackEventProcess).reorderCollection()\n      there was one(mockDragUiActions).endReorder()\n      there was one(mockCollectionProcess).reorderCollection(positionFrom, positionTo)\n    }\n\n    \"call reloadWorkspaces if startPositionReorderMode is equal to currentDraggingPosition and statuses.mode is ReorderMode\" in new DragJobsScope {\n\n      statuses = statuses.copy(\n        mode = ReorderMode,\n        startPositionReorderMode = positionFrom,\n        currentDraggingPosition = positionFrom)\n      mockTrackEventProcess.reorderCollection() returns serviceRight(Unit)\n      mockDragUiActions.endReorder() returns serviceRight(Unit)\n      mockWorkspaceUiActions.reloadWorkspaces(any, any) returns serviceRight(Unit)\n      mockLauncherDOM.getData returns seqLauncherData\n\n      dragJobs.dropReorder().mustRightUnit\n\n      there was no(mockTrackEventProcess).reorderCollection()\n      there was one(mockDragUiActions).endReorder()\n    }\n\n    \"call reloadWorkspaces if startPositionReorderMode is equal to currentDraggingPosition and statuses.mode is ReorderMode\" in new DragJobsScope {\n\n      statuses = statuses.copy(\n        mode = ReorderMode,\n        startPositionReorderMode = positionFrom,\n        currentDraggingPosition = positionFrom)\n      mockTrackEventProcess.reorderCollection() returns serviceRight(Unit)\n      mockDragUiActions.endReorder() returns serviceRight(Unit)\n      mockWorkspaceUiActions.reloadWorkspaces(any, any) returns serviceRight(Unit)\n      mockLauncherDOM.getData returns seqLauncherData.map(_.copy(collections = seqCollection))\n\n      dragJobs.dropReorder().mustRightUnit\n\n      there was no(mockTrackEventProcess).reorderCollection()\n      there was one(mockDragUiActions).endReorder()\n    }\n\n    \"Does nothing if  statuses.mode isn't ReorderMode\" in new DragJobsScope {\n\n      statuses = statuses.copy(mode = AddItemMode)\n      dragJobs.dropReorder().mustRightUnit\n    }\n  }\n\n  \"dropReorderException\" should {\n    \"call to reloadWorkspaces and showContactUsError\" in new DragJobsScope {\n\n      mockWorkspaceUiActions.reloadWorkspaces(any, any) returns serviceRight(Unit)\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n      mockLauncherDOM.getData returns seqLauncherData\n\n      dragJobs.dropReorderException()\n\n      there was one(mockNavigationUiActions).showContactUsError()\n    }\n  }\n\n  sequential\n  \"removeCollectionInReorderMode\" should {\n    \"show a message for remove collection if has a collection and can remove\" in new DragJobsScope {\n\n      statuses = statuses.copy(collectionReorderMode = Option(collection))\n      mockLauncherDOM.canRemoveCollections returns true\n      mockNavigationUiActions.showDialogForRemoveCollection(any) returns serviceRight(Unit)\n\n      dragJobs.removeCollectionInReorderMode().mustRightUnit\n\n      there was one(mockNavigationUiActions).showDialogForRemoveCollection(collection)\n      there was no(mockNavigationUiActions).showContactUsError()\n    }\n\n    \"show a message if has a collection and can't remove\" in new DragJobsScope {\n\n      statuses = statuses.copy(collectionReorderMode = Option(collection))\n      mockLauncherDOM.canRemoveCollections returns false\n      mockNavigationUiActions.showMinimumOneCollectionMessage() returns serviceRight(Unit)\n\n      dragJobs.removeCollectionInReorderMode().mustRightUnit\n\n      there was one(mockNavigationUiActions).showMinimumOneCollectionMessage()\n      there was no(mockNavigationUiActions).showContactUsError()\n    }\n\n    \"show a message error if statuses hasn't collectionReorder\" in new DragJobsScope {\n\n      statuses = statuses.copy(collectionReorderMode = None)\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n\n      dragJobs.removeCollectionInReorderMode().mustRightUnit\n\n      there was one(mockNavigationUiActions).showContactUsError()\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/launcher/jobs/LauncherJobsSpecification.scala",
    "content": "package cards.nine.app.ui.launcher.jobs\n\nimport cards.nine.app.di.Injector\nimport cards.nine.app.observers.ObserverRegister\nimport cards.nine.app.receivers.moments.MomentBroadcastReceiver\nimport cards.nine.app.ui.commons.states.MomentState\nimport cards.nine.app.ui.commons.{BroadAction, RequestCodes}\nimport cards.nine.app.ui.components.models.{\n  CollectionsWorkSpace,\n  LauncherData,\n  LauncherMoment,\n  MomentWorkSpace\n}\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.exceptions.{ChangeMomentException, LoadDataException}\nimport cards.nine.app.ui.launcher.jobs.uiactions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.{CollectionTestData, DockAppTestData, UserTestData}\nimport cards.nine.commons.utils.FileUtils\nimport cards.nine.models.types._\nimport cards.nine.process.accounts.UserAccountsProcess\nimport cards.nine.process.collection.CollectionProcess\nimport cards.nine.process.device.DeviceProcess\nimport cards.nine.process.intents.LauncherExecutorProcess\nimport cards.nine.process.moment.{MomentException, MomentProcess}\nimport cards.nine.process.recognition.RecognitionProcess\nimport cards.nine.process.theme.ThemeProcess\nimport cards.nine.process.thirdparty.ExternalServicesProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport cards.nine.process.user.{UserException, UserProcess}\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait LauncherJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait LauncherJobsScope\n      extends Scope\n      with LauncherTestData\n      with DockAppTestData\n      with CollectionTestData\n      with UserTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    lazy implicit val contextSupport = mock[ContextSupport]\n\n    val mockInjector = mock[Injector]\n\n    val mockLauncherDOM = mock[LauncherDOM]\n\n    val mockLauncherUiActions = mock[LauncherUiActions]\n\n    mockLauncherUiActions.dom returns mockLauncherDOM\n\n    val mockMenuDrawersUiActions = mock[MenuDrawersUiActions]\n\n    val mockAppDrawerUiActions = mock[AppDrawerUiActions]\n\n    mockAppDrawerUiActions.dom returns mockLauncherDOM\n\n    val mockNavigationUiActions = mock[NavigationUiActions]\n\n    val mockDockAppsUiActions = mock[DockAppsUiActions]\n\n    val mockTopBarUiActionss = mock[TopBarUiActions]\n\n    val mockWorkspaceUiActions = mock[WorkspaceUiActions]\n\n    val mockWidgetUiActions = mock[WidgetUiActions]\n\n    val mockDragUiActions = mock[DragUiActions]\n\n    mockDragUiActions.dom returns mockLauncherDOM\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val mockThemeProcess = mock[ThemeProcess]\n\n    mockInjector.themeProcess returns mockThemeProcess\n\n    val mockFileUtils = mock[FileUtils]\n\n    val mockCollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val mockLauncherExecutorProcess = mock[LauncherExecutorProcess]\n\n    mockInjector.launcherExecutorProcess returns mockLauncherExecutorProcess\n\n    val mockExternalServicesProcesss = mock[ExternalServicesProcess]\n\n    mockInjector.externalServicesProcess returns mockExternalServicesProcesss\n\n    val mockUserProcess = mock[UserProcess]\n\n    mockInjector.userProcess returns mockUserProcess\n\n    val mockUserAccountProcess = mock[UserAccountsProcess]\n\n    mockInjector.userAccountsProcess returns mockUserAccountProcess\n\n    val mockRecognitionProcess = mock[RecognitionProcess]\n\n    mockInjector.recognitionProcess returns mockRecognitionProcess\n\n    val mockObserverRegister = mock[ObserverRegister]\n\n    mockInjector.observerRegister returns mockObserverRegister\n\n    val mockMomentProcess = mock[MomentProcess]\n\n    mockInjector.momentProcess returns mockMomentProcess\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockMomentState = mock[MomentState]\n\n    val mockMomentBroadcastReceiver = mock[MomentBroadcastReceiver]\n\n    val launcherJobs = new LauncherJobs(\n      mockLauncherUiActions,\n      mockWorkspaceUiActions,\n      mockMenuDrawersUiActions,\n      mockAppDrawerUiActions,\n      mockNavigationUiActions,\n      mockDockAppsUiActions,\n      mockTopBarUiActionss,\n      mockWidgetUiActions,\n      mockDragUiActions) {\n\n      override lazy val di: Injector = mockInjector\n\n      override def sendBroadCastTask(broadAction: BroadAction) = TaskService.empty\n\n      override def getThemeTask = TaskService.right(theme)\n\n      override lazy val momentState = mockMomentState\n\n      override def momentBroadcastReceiver = mockMomentBroadcastReceiver\n    }\n  }\n\n}\n\nclass LauncherJobsSpec extends LauncherJobsSpecification {\n\n  sequential\n  \"initialize\" should {\n    \"Initialize all actions and services\" in new LauncherJobsScope {\n\n      mockLauncherUiActions.initialize() returns serviceRight(Unit)\n      mockThemeProcess.getTheme(any)(any) returns serviceRight(theme)\n\n      mockWidgetUiActions.initialize() returns serviceRight(Unit)\n      mockMenuDrawersUiActions.initialize() returns serviceRight(Unit)\n      mockAppDrawerUiActions.initialize() returns serviceRight(Unit)\n      mockTopBarUiActionss.initialize() returns serviceRight(Unit)\n      mockLauncherUiActions.initialize() returns serviceRight(Unit)\n\n      mockExternalServicesProcesss.initializeStrictMode(any) returns serviceRight(Unit)\n      mockExternalServicesProcesss.initializeCrashlytics(any) returns serviceRight(Unit)\n      mockExternalServicesProcesss.initializeFirebase(any) returns serviceRight(Unit)\n      mockExternalServicesProcesss.initializeStetho(any) returns serviceRight(Unit)\n\n      mockUserProcess.register(any) returns serviceRight(Unit)\n\n      launcherJobs.initialize().mustRightUnit\n    }.pendingUntilFixed\n  }\n\n  \"resume\" should {\n    \"load the launcher if there aren't any collections\" in new LauncherJobsScope {\n\n      mockObserverRegister.registerObserverTask() returns serviceRight(Unit)\n      mockLauncherDOM.isEmptyCollections returns true\n      mockObserverRegister.registerObserverTask() returns serviceRight(Unit)\n      mockRecognitionProcess.getWeather returns serviceRight(weatherState)\n      mockWorkspaceUiActions.showWeather(any) returns serviceRight(Unit)\n\n      mockCollectionProcess.getCollections returns serviceRight(seqCollection)\n      mockDeviceProcess.getDockApps returns serviceRight(seqDockApp)\n      mockMomentState.getPersistMoment returns Option(HomeMorningMoment)\n      mockMomentProcess.fetchMomentByType(any) returns serviceRight(Option(moment))\n\n      mockUserProcess.getUser(any) returns serviceRight(user)\n      mockMenuDrawersUiActions.loadUserProfileMenu(any, any, any, any) returns serviceRight(Unit)\n\n      mockWorkspaceUiActions.loadLauncherInfo(any) returns serviceRight(Unit)\n      mockDockAppsUiActions.loadDockApps(any) returns serviceRight(Unit)\n      mockTopBarUiActionss.loadBar(any) returns serviceRight(Unit)\n      mockMenuDrawersUiActions.reloadBarMoment(any) returns serviceRight(Unit)\n\n      launcherJobs.resume().mustRightUnit\n\n      there was one(mockObserverRegister).registerObserverTask()\n    }.pendingUntilFixed\n\n    \"return a LoadDataException when loadLauncher \" in new LauncherJobsScope {\n\n      mockObserverRegister.registerObserverTask() returns serviceRight(Unit)\n      mockLauncherDOM.isEmptyCollections returns true\n      mockRecognitionProcess.getWeather returns serviceRight(weatherState)\n      mockWorkspaceUiActions.showWeather(any) returns serviceRight(Unit)\n      mockObserverRegister.registerObserverTask() returns serviceRight(Unit)\n\n      mockCollectionProcess.getCollections returns serviceRight(seqCollection)\n      mockDeviceProcess.getDockApps returns serviceRight(seqDockApp)\n      mockMomentState.getPersistMoment returns None\n      mockMomentProcess.getBestAvailableMoment(any, any)(any) returns serviceRight(Option(moment))\n      mockUserProcess.getUser(any) returns serviceLeft(UserException(\"\"))\n\n      launcherJobs.resume().mustLeft[LoadDataException]\n      there was one(mockObserverRegister).registerObserverTask()\n    }.pendingUntilFixed\n\n    \"call to changeMomentIfIsAvailable if has collections.\" in new LauncherJobsScope {\n\n      mockObserverRegister.registerObserverTask() returns serviceRight(Unit)\n      mockLauncherDOM.isEmptyCollections returns false\n      mockMomentState.nonPersist returns true\n      mockMomentProcess.getBestAvailableMoment(any, any)(any) returns serviceRight(Option(moment))\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(Option(collection))\n      mockLauncherDOM.getCurrentMomentType returns Option(WorkMoment)\n      mockWorkspaceUiActions.reloadMoment(any) returns serviceRight(Unit)\n      mockObserverRegister.registerObserverTask() returns serviceRight(Unit)\n      mockRecognitionProcess.getWeather returns serviceRight(weatherState)\n      mockWorkspaceUiActions.showWeather(any) returns serviceRight(Unit)\n\n      launcherJobs.resume().mustRightUnit\n\n      there was one(mockObserverRegister).registerObserverTask()\n    }.pendingUntilFixed\n\n    \"return a ChangeMomentException when change a moment\" in new LauncherJobsScope {\n\n      mockObserverRegister.registerObserverTask() returns serviceRight(Unit)\n      mockLauncherDOM.isEmptyCollections returns false\n      mockMomentProcess.getBestAvailableMoment(any, any)(any) returns serviceLeft(\n        MomentException(\"\"))\n      mockObserverRegister.registerObserverTask() returns serviceRight(Unit)\n      mockRecognitionProcess.getWeather returns serviceRight(weatherState)\n      mockWorkspaceUiActions.showWeather(any) returns serviceRight(Unit)\n\n      launcherJobs.resume().mustLeft[ChangeMomentException]\n\n      there was one(mockObserverRegister).registerObserverTask()\n    }.pendingUntilFixed\n  }\n\n  \"registerFence\" should {\n    \"register a fence\" in new LauncherJobsScope {\n\n      mockRecognitionProcess.registerFenceUpdates(any, any)(any) returns serviceRight(Unit)\n      launcherJobs.registerFence().mustRightUnit\n      there was one(mockRecognitionProcess)\n        .registerFenceUpdates(===(MomentBroadcastReceiver.momentFenceAction), any)(any)\n    }\n  }\n\n  \"unregisterFence\" should {\n    \"unregister a fence\" in new LauncherJobsScope {\n\n      mockRecognitionProcess.unregisterFenceUpdates(any)(any) returns serviceRight(Unit)\n      launcherJobs.unregisterFence().mustRightUnit\n      there was one(mockRecognitionProcess).unregisterFenceUpdates(\n        ===(MomentBroadcastReceiver.momentFenceAction))(any)\n    }\n  }\n\n  \"reloadFence\" should {\n    \"unregister and registers a fence\" in new LauncherJobsScope {\n      mockRecognitionProcess.unregisterFenceUpdates(any)(any) returns serviceRight(Unit)\n      mockRecognitionProcess.registerFenceUpdates(any, any)(any) returns serviceRight(Unit)\n\n      launcherJobs.reloadFence().mustRightUnit\n\n      there was one(mockRecognitionProcess).unregisterFenceUpdates(any)(any)\n      there was one(mockRecognitionProcess).registerFenceUpdates(any, any)(any)\n    }\n  }\n\n  \"pause\" should {\n    \"call to unregisterObserverTask\" in new LauncherJobsScope {\n\n      mockObserverRegister.unregisterObserverTask() returns serviceRight(Unit)\n      launcherJobs.pause().mustRightUnit\n      there was one(mockObserverRegister).unregisterObserverTask()\n    }\n  }\n\n  \"destroy\" should {\n    \"call to destroy\" in new LauncherJobsScope {\n      mockWidgetUiActions.destroy() returns serviceRight(Unit)\n      launcherJobs.destroy().mustRightUnit\n      there was one(mockWidgetUiActions).destroy()\n    }\n  }\n\n  \"reloadAppsMomentBar\" should {\n    \"reload apps there is a moment associated with a collection\" in new LauncherJobsScope {\n\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n      mockLauncherDOM.getCurrentMomentType returns Option(HomeMorningMoment)\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(Option(collection))\n      mockMenuDrawersUiActions.reloadBarMoment(any) returns serviceRight(Unit)\n\n      launcherJobs.reloadAppsMomentBar().mustRightUnit\n\n      there was one(mockMomentProcess).getMoments\n      there was one(mockLauncherDOM).getCurrentMomentType\n      there was one(mockCollectionProcess).getCollectionById(moment.collectionId.getOrElse(0))\n      there was one(mockMenuDrawersUiActions).reloadBarMoment(\n        LauncherMoment(Option(HomeMorningMoment), Option(collection)))\n    }\n\n    \"reload apps when there isn't a moment associated with a collection\" in new LauncherJobsScope {\n\n      mockMomentProcess.getMoments returns serviceRight(\n        seqMoment map (_.copy(collectionId = None)))\n      mockLauncherDOM.getCurrentMomentType returns Option(HomeMorningMoment)\n      mockMenuDrawersUiActions.reloadBarMoment(any) returns serviceRight(Unit)\n\n      launcherJobs.reloadAppsMomentBar().mustRightUnit\n\n      there was one(mockMomentProcess).getMoments\n      there was one(mockLauncherDOM).getCurrentMomentType\n      there was no(mockCollectionProcess).getCollectionById(any)\n      there was one(mockMenuDrawersUiActions).reloadBarMoment(\n        LauncherMoment(Option(HomeMorningMoment), None))\n    }\n  }\n\n  \"loadLauncherInfo\" should {\n    \"load launcherInfo when the service returns a right response and there are collections\" in new LauncherJobsScope {\n\n      mockCollectionProcess.getCollections returns serviceRight(seqCollection)\n      mockDeviceProcess.getDockApps returns serviceRight(seqDockApp)\n      mockMomentState.getPersistMoment returns Option(HomeMorningMoment)\n      mockMomentProcess.fetchMomentByType(any) returns serviceRight(Option(moment))\n\n      mockUserProcess.getUser(any) returns serviceRight(user)\n      mockMenuDrawersUiActions.loadUserProfileMenu(any, any, any, any) returns serviceRight(Unit)\n\n      mockWorkspaceUiActions.loadLauncherInfo(any) returns serviceRight(Unit)\n      mockDockAppsUiActions.loadDockApps(any) returns serviceRight(Unit)\n      mockTopBarUiActionss.loadBar(any) returns serviceRight(Unit)\n      mockMenuDrawersUiActions.reloadBarMoment(any) returns serviceRight(Unit)\n\n      launcherJobs.loadLauncherInfo().mustRightUnit\n\n      there was one(mockCollectionProcess).getCollections\n      there was one(mockDeviceProcess).getDockApps\n      there was one(mockMomentState).getPersistMoment\n      there was one(mockMomentProcess).fetchMomentByType(HomeMorningMoment)\n      there was one(mockUserProcess).getUser(any)\n      there was one(mockMenuDrawersUiActions).loadUserProfileMenu(\n        user.email,\n        user.userProfile.name,\n        user.userProfile.avatar,\n        user.userProfile.cover)\n\n    }\n\n    \"load launcherInfo when the service returns a right response, there are collections but there isn't a persist moment\" in new LauncherJobsScope {\n\n      mockCollectionProcess.getCollections returns serviceRight(seqCollection)\n      mockDeviceProcess.getDockApps returns serviceRight(seqDockApp)\n      mockMomentState.getPersistMoment returns None\n      mockMomentProcess.getBestAvailableMoment(any, any)(any) returns serviceRight(Option(moment))\n\n      mockUserProcess.getUser(any) returns serviceRight(user)\n      mockMenuDrawersUiActions.loadUserProfileMenu(any, any, any, any) returns serviceRight(Unit)\n\n      mockWorkspaceUiActions.loadLauncherInfo(any) returns serviceRight(Unit)\n      mockDockAppsUiActions.loadDockApps(any) returns serviceRight(Unit)\n      mockTopBarUiActionss.loadBar(any) returns serviceRight(Unit)\n      mockMenuDrawersUiActions.reloadBarMoment(any) returns serviceRight(Unit)\n\n      launcherJobs.loadLauncherInfo().mustRightUnit\n\n      there was one(mockCollectionProcess).getCollections\n      there was one(mockDeviceProcess).getDockApps\n      there was one(mockMomentState).getPersistMoment\n      there was no(mockMomentProcess).fetchMomentByType(HomeMorningMoment)\n      there was one(mockMomentProcess).getBestAvailableMoment(any, any)(any)\n      there was one(mockUserProcess).getUser(any)\n      there was one(mockMenuDrawersUiActions).loadUserProfileMenu(\n        user.email,\n        user.userProfile.name,\n        user.userProfile.avatar,\n        user.userProfile.cover)\n\n    }\n\n    \"load launcher and don't show the user info if UserService return an UserException\" in new LauncherJobsScope {\n\n      mockCollectionProcess.getCollections returns serviceRight(seqCollection)\n      mockDeviceProcess.getDockApps returns serviceRight(seqDockApp)\n      mockMomentState.getPersistMoment returns None\n      mockMomentProcess.getBestAvailableMoment(any, any)(any) returns serviceRight(Option(moment))\n      mockUserProcess.getUser(any) returns serviceLeft(UserException(\"\"))\n      mockWorkspaceUiActions.loadLauncherInfo(any) returns serviceRight(Unit)\n      mockDockAppsUiActions.loadDockApps(any) returns serviceRight(Unit)\n      mockTopBarUiActionss.loadBar(any) returns serviceRight(Unit)\n      mockMenuDrawersUiActions.reloadBarMoment(any) returns serviceRight(Unit)\n\n      launcherJobs.loadLauncherInfo().mustRightUnit\n\n      there was one(mockCollectionProcess).getCollections\n      there was one(mockDeviceProcess).getDockApps\n      there was one(mockMomentState).getPersistMoment\n      there was no(mockMomentProcess).fetchMomentByType(HomeMorningMoment)\n      there was no(mockMenuDrawersUiActions).loadUserProfileMenu(any, any, any, any)\n    }\n\n    \"return LoadDataException if there aren't any collections\" in new LauncherJobsScope {\n\n      mockCollectionProcess.getCollections returns serviceRight(Seq.empty)\n      mockDeviceProcess.getDockApps returns serviceRight(seqDockApp)\n      mockMomentState.getPersistMoment returns Option(HomeMorningMoment)\n      mockMomentProcess.fetchMomentByType(any) returns serviceRight(Option(moment))\n\n      launcherJobs.loadLauncherInfo().mustLeft[LoadDataException]\n\n      there was one(mockCollectionProcess).getCollections\n      there was one(mockDeviceProcess).getDockApps\n      there was one(mockMomentState).getPersistMoment\n      there was one(mockMomentProcess).fetchMomentByType(HomeMorningMoment)\n    }\n  }\n\n  sequential\n  \"changeMomentIfIsAvailable\" should {\n\n    \"Do nothing if the best Available Moment is equal to current moment\" in new LauncherJobsScope {\n\n      mockMomentState.nonPersist returns true\n      mockMomentProcess.getBestAvailableMoment(===(None), ===(None))(any) returns serviceRight(\n        Option(moment))\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(Option(collection))\n      mockLauncherDOM.getCurrentMomentType returns Option(HomeMorningMoment)\n\n      launcherJobs.changeMomentIfIsAvailable(true, None).mustRightUnit\n\n      there was one(mockMomentProcess).getBestAvailableMoment(===(None), ===(None))(any)\n    }\n\n    \"Reload moment if the best Available Moment isn't equal to current moment\" in new LauncherJobsScope {\n\n      mockMomentState.nonPersist returns true\n      mockMomentProcess.getBestAvailableMoment(any, any)(any) returns serviceRight(Option(moment))\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(Option(collection))\n      mockLauncherDOM.getCurrentMomentType returns Option(WorkMoment)\n      mockWorkspaceUiActions.reloadMoment(any) returns serviceRight(Unit)\n\n      launcherJobs.changeMomentIfIsAvailable(true, None).mustRightUnit\n\n      there was one(mockMomentProcess).getBestAvailableMoment(===(None), ===(None))(any)\n    }\n\n    \"Reload workspace with new date when haveHeadphonesFence\" in new LauncherJobsScope {\n\n      mockMomentState.nonPersist returns true\n      mockMomentProcess.getBestAvailableMoment(any, any)(any) returns serviceRight(Option(moment))\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(Option(collection))\n      mockLauncherDOM.getCurrentMomentType returns Option(WorkMoment)\n      mockWorkspaceUiActions.reloadMoment(any) returns serviceRight(Unit)\n\n      launcherJobs.changeMomentIfIsAvailable(false, Option(HeadphonesFence.keyIn)).mustRightUnit\n\n      there was one(mockMomentProcess).getBestAvailableMoment(===(Option(true)), ===(None))(any)\n    }\n\n    \"Reload workspace with new date when HeadphonesFence.keyOut\" in new LauncherJobsScope {\n\n      mockMomentState.nonPersist returns true\n      mockMomentProcess.getBestAvailableMoment(any, any)(any) returns serviceRight(Option(moment))\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(Option(collection))\n      mockLauncherDOM.getCurrentMomentType returns Option(WorkMoment)\n      mockWorkspaceUiActions.reloadMoment(any) returns serviceRight(Unit)\n\n      launcherJobs.changeMomentIfIsAvailable(false, Option(HeadphonesFence.keyOut)).mustRightUnit\n\n      there was one(mockMomentProcess).getBestAvailableMoment(===(Option(false)), ===(None))(any)\n    }\n\n    \"Reload workspace with new date when InVehicleFence.key\" in new LauncherJobsScope {\n\n      mockMomentState.nonPersist returns true\n      mockMomentProcess.getBestAvailableMoment(any, any)(any) returns serviceRight(Option(moment))\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(Option(collection))\n      mockLauncherDOM.getCurrentMomentType returns Option(WorkMoment)\n      mockWorkspaceUiActions.reloadMoment(any) returns serviceRight(Unit)\n\n      launcherJobs.changeMomentIfIsAvailable(false, Option(InVehicleFence.key)).mustRightUnit\n\n      there was one(mockMomentProcess)\n        .getBestAvailableMoment(===(None), ===(Option(InVehicleActivity)))(any)\n    }\n\n  }\n\n  \"changeMoment\" should {\n    \"change the current Moment with a collection associated\" in new LauncherJobsScope {\n\n      mockTrackEventProcess.changeMoment(any) returns serviceRight(Unit)\n      mockMomentProcess.findMoment(any) returns serviceRight(Option(moment))\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(Option(collection))\n      mockWorkspaceUiActions.reloadMoment(any) returns serviceRight(Unit)\n\n      launcherJobs.changeMoment(moment.id).mustRightUnit\n\n      there was one(mockTrackEventProcess).changeMoment(moment.momentType.name)\n      there was one(mockMomentProcess).findMoment(moment.id)\n      there was one(mockCollectionProcess).getCollectionById(moment.collectionId.getOrElse(0))\n      there was one(mockWorkspaceUiActions).reloadMoment(\n        LauncherData(\n          MomentWorkSpace,\n          Option(LauncherMoment(Option(moment.momentType), Option(collection)))))\n    }\n\n    \"change the Moment without a collection associated\" in new LauncherJobsScope {\n\n      mockTrackEventProcess.changeMoment(any) returns serviceRight(Unit)\n      mockMomentProcess.findMoment(any) returns serviceRight(\n        Option(moment.copy(collectionId = None)))\n      mockWorkspaceUiActions.reloadMoment(any) returns serviceRight(Unit)\n\n      launcherJobs.changeMoment(moment.id).mustRightUnit\n\n      there was one(mockTrackEventProcess).changeMoment(moment.momentType.name)\n      there was one(mockMomentProcess).findMoment(moment.id)\n      there was no(mockCollectionProcess).getCollectionById(any)\n      there was one(mockWorkspaceUiActions).reloadMoment(\n        LauncherData(MomentWorkSpace, Option(LauncherMoment(Option(moment.momentType), None))))\n    }\n\n    \"return an exception when the moment with momentId has not been found\" in new LauncherJobsScope {\n\n      mockTrackEventProcess.changeMoment(any) returns serviceRight(Unit)\n      mockMomentProcess.findMoment(any) returns serviceLeft(MomentException(\"\"))\n      launcherJobs.changeMoment(moment.id).mustLeft[MomentException]\n      there was no(mockTrackEventProcess).changeMoment(moment.momentType.name)\n      there was one(mockMomentProcess).findMoment(moment.id)\n    }\n  }\n\n  \"cleanPersistedMoment\" should {\n    \"call to clean\" in new LauncherJobsScope {\n\n      mockTrackEventProcess.unpinMoment() returns serviceRight(Unit)\n      launcherJobs.cleanPersistedMoment().mustRightUnit\n      there was one(mockTrackEventProcess).unpinMoment()\n      there was one(mockMomentState).clean()\n    }\n  }\n\n  \"reloadCollection\" should {\n    \"Reload a collection with a valid collectionId\" in new LauncherJobsScope {\n\n      mockCollectionProcess.getCollectionById(any) returns serviceRight(Option(collection))\n      mockLauncherDOM.getData returns seqLauncherData.map(\n        _.copy(collections = seqCollection, workSpaceType = CollectionsWorkSpace))\n      mockWorkspaceUiActions.reloadWorkspaces(any, any) returns serviceRight(Unit)\n\n      launcherJobs.reloadCollection(collection.id).mustRightUnit\n\n      there was one(mockCollectionProcess).getCollectionById(collection.id)\n    }\n  }\n\n  \"addCollection\" should {\n    \"Add the collection when workSpaceType is CollectionsWorkSpace\" in new LauncherJobsScope {\n\n      mockLauncherDOM.getData returns seqLauncherData.map(\n        _.copy(collections = seqCollection, workSpaceType = CollectionsWorkSpace))\n      mockWorkspaceUiActions.reloadWorkspaces(any, any) returns serviceRight(Unit)\n\n      launcherJobs.addCollection(collection).mustRightUnit\n\n    }\n    \"do nothing when workSpaceType isn't CollectionsWorkSpace\" in new LauncherJobsScope {\n\n      mockLauncherDOM.getData returns seqLauncherData.map(_.copy(collections = seqCollection))\n      launcherJobs.addCollection(collection).mustRightUnit\n\n    }\n  }\n\n  \"updateCollection\" should {\n    \"update a collection when the service returns a right response\" in new LauncherJobsScope {\n\n      mockLauncherDOM.getData returns seqLauncherData.map(\n        _.copy(collections = seqCollection, workSpaceType = CollectionsWorkSpace))\n      mockWorkspaceUiActions.reloadWorkspaces(any, any) returns serviceRight(Unit)\n\n      launcherJobs.updateCollection(collection).mustRightUnit\n\n      there was one(mockWorkspaceUiActions).reloadWorkspaces(any, any)\n    }\n\n    \"update the collection when there are collections but a collection with this position has not been found\" in new LauncherJobsScope {\n\n      mockLauncherDOM.getData returns seqLauncherData.map(\n        _.copy(collections = seqCollection, workSpaceType = CollectionsWorkSpace))\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n\n      launcherJobs.updateCollection(collection.copy(position = 30)).mustRightUnit\n\n      there was no(mockWorkspaceUiActions).reloadWorkspaces(any, any)\n    }\n\n    \"Do nothing when there aren't any collections\" in new LauncherJobsScope {\n\n      mockLauncherDOM.getData returns seqLauncherData.map(\n        _.copy(collections = Seq.empty, workSpaceType = CollectionsWorkSpace))\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n\n      launcherJobs.updateCollection(collection).mustRightUnit\n\n      there was no(mockWorkspaceUiActions).reloadWorkspaces(any, any)\n    }\n  }\n\n  \"removeCollection\" should {\n    \"Do nothing if workSpaceType isn't CollectionsWorkSpace\" in new LauncherJobsScope {\n\n      mockTrackEventProcess.deleteCollection(any) returns serviceRight(Unit)\n      mockCollectionProcess.deleteCollection(any) returns serviceRight(Unit)\n      mockLauncherDOM.getData returns seqLauncherData.map(_.copy(collections = seqCollection))\n      mockWorkspaceUiActions.reloadWorkspaces(any, any) returns serviceRight(Unit)\n\n      launcherJobs.removeCollection(collection).mustRightUnit\n\n      there was one(mockTrackEventProcess).deleteCollection(collection.name)\n      there was one(mockCollectionProcess).deleteCollection(collection.id)\n      there was one(mockWorkspaceUiActions)\n        .reloadWorkspaces(Seq.empty, Option(launcherJobs.defaultPage))\n\n    }\n\n    \"Remove the collection\" in new LauncherJobsScope {\n\n      mockTrackEventProcess.deleteCollection(any) returns serviceRight(Unit)\n      mockCollectionProcess.deleteCollection(any) returns serviceRight(Unit)\n      mockLauncherDOM.getData returns seqLauncherData.map(\n        _.copy(collections = seqCollection, workSpaceType = CollectionsWorkSpace))\n      mockWorkspaceUiActions.reloadWorkspaces(any, any) returns serviceRight(Unit)\n\n      launcherJobs.removeCollection(collection).mustRightUnit\n\n      there was one(mockTrackEventProcess).deleteCollection(collection.name)\n      there was one(mockCollectionProcess).deleteCollection(collection.id)\n    }\n  }\n\n  \"removeMomentDialog\" should {\n    \"Show a  message indicating it can't remove the moment OutAndAboutMoment\" in new LauncherJobsScope {\n\n      mockNavigationUiActions.showCantRemoveOutAndAboutMessage() returns serviceRight(Unit)\n      launcherJobs.removeMomentDialog(OutAndAboutMoment, moment.id).mustRightUnit\n      there was one(mockNavigationUiActions).showCantRemoveOutAndAboutMessage()\n\n    }\n\n    \"Show a message for remove moment\" in new LauncherJobsScope {\n\n      mockNavigationUiActions.showDialogForRemoveMoment(any) returns serviceRight(Unit)\n      launcherJobs.removeMomentDialog(moment.momentType, moment.id).mustRightUnit\n      there was one(mockNavigationUiActions).showDialogForRemoveMoment(moment.id)\n    }\n  }\n\n  \"removeMoment\" should {\n    \"return a valid response when the service returns a right response\" in new LauncherJobsScope {\n\n      mockTrackEventProcess.deleteMoment() returns serviceRight(Unit)\n      mockTrackEventProcess.unpinMoment() returns serviceRight(Unit)\n      mockMomentProcess.deleteMoment(any) returns serviceRight(Unit)\n      mockRecognitionProcess.unregisterFenceUpdates(any)(any) returns serviceRight(Unit)\n      mockRecognitionProcess.registerFenceUpdates(any, any)(any) returns serviceRight(Unit)\n\n      launcherJobs.removeMoment(moment.id).mustRightUnit\n\n      there was one(mockTrackEventProcess).unpinMoment()\n      there was one(mockTrackEventProcess).deleteMoment()\n      there was one(mockMomentProcess).deleteMoment(moment.id)\n    }\n  }\n\n  sequential\n  \"requestPermissionsResult\" should {\n    \"call to reloadContacts for the specified permissions: contactsPermission\" in new LauncherJobsScope {\n\n      mockUserAccountProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(PermissionResult(ReadContacts, result = true)))\n      mockAppDrawerUiActions.reloadContacts() returns serviceRight(Unit)\n\n      launcherJobs\n        .requestPermissionsResult(\n          RequestCodes.contactsPermission,\n          Array(GetAccounts.value, ReadContacts.value),\n          Array.empty)\n        .mustRightUnit\n    }\n    \"call to reloadContacts for the specified permissions: callLogPermission\" in new LauncherJobsScope {\n\n      mockUserAccountProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(PermissionResult(ReadCallLog, result = true)))\n      mockAppDrawerUiActions.reloadContacts() returns serviceRight(Unit)\n\n      launcherJobs\n        .requestPermissionsResult(\n          RequestCodes.callLogPermission,\n          Array(ReadCallLog.value),\n          Array.empty)\n        .mustRightUnit\n    }\n    \"call to launcherExecutorProcess for the specified permissions: phoneCallPermission \" in new LauncherJobsScope {\n\n      mockUserAccountProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(PermissionResult(CallPhone, result = true)))\n      statuses = statuses.copy(lastPhone = Option(lastPhone))\n      mockLauncherExecutorProcess.execute(any)(any) returns serviceRight(Unit)\n\n      launcherJobs\n        .requestPermissionsResult(\n          RequestCodes.phoneCallPermission,\n          Array(CallPhone.value),\n          Array.empty)\n        .mustRightUnit\n    }\n\n    \"Does nothing for the specified permissions :phoneCallPermission if hasn't lastPhone\" in new LauncherJobsScope {\n\n      mockUserAccountProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(PermissionResult(CallPhone, result = true)))\n      statuses = statuses.copy(lastPhone = None)\n      launcherJobs\n        .requestPermissionsResult(\n          RequestCodes.phoneCallPermission,\n          Array(CallPhone.value),\n          Array.empty)\n        .mustRightUnit\n    }\n\n    \"Show a message error and try to request the permission with contactsPermission\" in new LauncherJobsScope {\n\n      mockUserAccountProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(PermissionResult(ReadContacts, result = false)))\n      mockAppDrawerUiActions.reloadApps() returns serviceRight(Unit)\n      mockNavigationUiActions.showContactPermissionError(any) returns serviceRight(Unit)\n      mockUserAccountProcess.requestPermission(any, any)(any) returns serviceRight(Unit)\n\n      launcherJobs\n        .requestPermissionsResult(\n          RequestCodes.contactsPermission,\n          Array(GetAccounts.value, ReadContacts.value),\n          Array.empty)\n        .mustRightUnit\n    }\n\n    \"Show a message error and try to request the permission with callLogPermission\" in new LauncherJobsScope {\n\n      mockUserAccountProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(PermissionResult(ReadCallLog, result = false)))\n      mockAppDrawerUiActions.reloadApps() returns serviceRight(Unit)\n      mockNavigationUiActions.showCallPermissionError(any) returns serviceRight(Unit)\n      mockUserAccountProcess.requestPermission(any, any)(any) returns serviceRight(Unit)\n\n      launcherJobs\n        .requestPermissionsResult(\n          RequestCodes.callLogPermission,\n          Array(ReadCallLog.value),\n          Array.empty)\n        .mustRightUnit\n    }\n\n    \"Show a message error if haven't permissions phoneCallPermission \" in new LauncherJobsScope {\n\n      mockUserAccountProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(PermissionResult(CallPhone, result = false)))\n      statuses = statuses.copy(lastPhone = Option(lastPhone))\n      mockLauncherExecutorProcess.launchDial(any)(any) returns serviceRight(Unit)\n      mockNavigationUiActions.showNoPhoneCallPermissionError() returns serviceRight(Unit)\n\n      launcherJobs\n        .requestPermissionsResult(\n          RequestCodes.phoneCallPermission,\n          Array(CallPhone.value),\n          Array.empty)\n        .mustRightUnit\n    }\n\n    \"Do nothing for the specified permissions :phoneCallPermission if hasn't lastPhone \" in new LauncherJobsScope {\n\n      mockUserAccountProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(PermissionResult(CallPhone, result = false)))\n      statuses = statuses.copy(lastPhone = None)\n\n      launcherJobs\n        .requestPermissionsResult(\n          RequestCodes.phoneCallPermission,\n          Array(CallPhone.value),\n          Array.empty)\n        .mustRightUnit\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/launcher/jobs/LauncherTestData.scala",
    "content": "package cards.nine.app.ui.launcher.jobs\n\nimport android.graphics.Color\nimport cards.nine.app.ui.components.models.{LauncherData, LauncherMoment, MomentWorkSpace}\nimport cards.nine.commons.test.data.{ApplicationTestData, DeviceTestData}\nimport cards.nine.models._\nimport cards.nine.models.types.theme._\nimport cards.nine.models.types.{ClearCondition, CloudyCondition, FoggyCondition, NineCardsMoment}\nimport cards.nine.services.persistence.conversions.AppConversions\n\nimport scala.util.Random\n\ntrait LauncherTestData extends DeviceTestData with ApplicationTestData with AppConversions {\n\n  val idWidget    = 1\n  val appWidgetId = 1\n\n  val launcherMoment =\n    LauncherMoment(momentType = Option(NineCardsMoment.defaultMoment), collection = None)\n\n  def launcherData(num: Int = 0) =\n    LauncherData(\n      workSpaceType = MomentWorkSpace,\n      moment = Option(launcherMoment),\n      collections = Seq.empty,\n      positionByType = 0 + num)\n\n  val launcherData: LauncherData         = launcherData(0)\n  val seqLauncherData: Seq[LauncherData] = Seq(launcherData(0), launcherData(1), launcherData(2))\n\n  val numberPhone = \"123456789\"\n  val packageName = \"packageName\"\n  val errorMenu   = 0\n\n  val keyword: String = \"keyword\"\n  val querry: String  = \"querry\"\n\n  val position: Int            = 1\n  val positionFrom: Int        = 1\n  val positionFromNoExist: Int = 50\n  val positionTo: Int          = 2\n\n  val theme = NineCardsTheme(\n    name = \"light\",\n    parent = ThemeLight,\n    styles = Seq.empty,\n    themeColors = ThemeColors(Color.parseColor(\"#FF9800\"), Seq.empty))\n\n  val humidity              = Random.nextInt(100)\n  val dewPointCelsius       = Random.nextFloat()\n  val dewPointFahrenheit    = Random.nextFloat()\n  val temperatureCelsius    = Random.nextFloat()\n  val temperatureFahrenheit = Random.nextFloat()\n  val conditionsServices    = Seq(ClearCondition, CloudyCondition, FoggyCondition)\n\n  val weatherState = WeatherState(\n    conditions = conditionsServices,\n    humidity = humidity,\n    dewPointCelsius = dewPointCelsius,\n    dewPointFahrenheit = dewPointFahrenheit,\n    temperatureCelsius = temperatureCelsius,\n    temperatureFahrenheit = temperatureFahrenheit)\n\n  val lastPhone = \"lastPhone\"\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/launcher/jobs/NavigationJobsSpecification.scala",
    "content": "package cards.nine.app.ui.launcher.jobs\n\nimport android.graphics.Point\nimport android.os.Bundle\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.RequestCodes\nimport cards.nine.app.ui.components.layouts.LauncherWorkSpaces\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.jobs.uiactions._\nimport cards.nine.app.ui.launcher.{EditWidgetsMode, MoveTransformation, NormalMode}\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.{\n  ApplicationTestData,\n  CardTestData,\n  CollectionTestData,\n  DeviceTestData,\n  DockAppTestData\n}\nimport cards.nine.models.NineCardsIntent\nimport cards.nine.models.types._\nimport cards.nine.process.accounts.UserAccountsProcess\nimport cards.nine.process.intents.LauncherExecutorProcess\nimport cards.nine.process.moment.MomentProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport com.fortysevendeg.ninecardslauncher.R\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait NavigationJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait NavigationJobsScope\n      extends Scope\n      with LauncherTestData\n      with CollectionTestData\n      with ApplicationTestData\n      with DeviceTestData\n      with DockAppTestData\n      with CardTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockInjector = mock[Injector]\n\n    val mockLauncherDOM = mock[LauncherDOM]\n\n    val mockLauncherWorkspaces = mock[LauncherWorkSpaces]\n\n    mockLauncherDOM.workspaces returns mockLauncherWorkspaces\n\n    val mockNavigationUiActions = mock[NavigationUiActions]\n\n    mockNavigationUiActions.dom returns mockLauncherDOM\n\n    val mockAppDrawerUiActions = mock[AppDrawerUiActions]\n\n    val mockMenuDrawersUiActions = mock[MenuDrawersUiActions]\n\n    val mockWidgetUiActions = mock[WidgetUiActions]\n\n    val mockLauncherExecutorProcess = mock[LauncherExecutorProcess]\n\n    mockInjector.launcherExecutorProcess returns mockLauncherExecutorProcess\n\n    val mockMomentProcess = mock[MomentProcess]\n\n    mockInjector.momentProcess returns mockMomentProcess\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockUserAccountsProcess = mock[UserAccountsProcess]\n\n    mockInjector.userAccountsProcess returns mockUserAccountsProcess\n\n    val mockPoint = mock[Point]\n\n    val navigationJobs = new NavigationJobs(\n      mockNavigationUiActions,\n      mockAppDrawerUiActions,\n      mockMenuDrawersUiActions,\n      mockWidgetUiActions)(contextWrapper) {\n\n      override lazy val di: Injector = mockInjector\n\n      override def getColor(res: Int): Int = 0\n\n    }\n  }\n\n}\n\nclass NavigationJobsSpec extends NavigationJobsSpecification {\n  sequential\n  \"openMenu\" should {\n    \"return a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockMenuDrawersUiActions.openMenu() returns serviceRight(Unit)\n      navigationJobs.openMenu().mustRightUnit\n      there was one(mockMenuDrawersUiActions).openMenu()\n    }\n  }\n\n  \"launchCreateOrCollection\" should {\n    \"return a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockNavigationUiActions.launchCreateOrCollection(any[Bundle]) returns serviceRight(Unit)\n      navigationJobs.launchCreateOrCollection().mustRightUnit\n      there was one(mockNavigationUiActions).launchCreateOrCollection(any)\n    }\n  }\n\n  \"launchPrivateCollection\" should {\n    \"return a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockNavigationUiActions.launchPrivateCollection(any[Bundle]) returns serviceRight(Unit)\n      navigationJobs.launchPrivateCollection().mustRightUnit\n      there was one(mockNavigationUiActions).launchPrivateCollection(any)\n    }\n  }\n\n  \"launchPublicCollection\" should {\n    \"return a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockNavigationUiActions.launchPublicCollection(any[Bundle]) returns serviceRight(Unit)\n      navigationJobs.launchPublicCollection().mustRightUnit\n      there was one(mockNavigationUiActions).launchPublicCollection(any)\n    }\n  }\n\n  \"launchAddMoment\" should {\n    \"return a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockNavigationUiActions.launchAddMoment(any[Bundle]) returns serviceRight(Unit)\n      navigationJobs.launchAddMoment().mustRightUnit\n      there was one(mockNavigationUiActions).launchAddMoment(any)\n    }\n  }\n\n  \"launchEditMoment\" should {\n    \"return a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockNavigationUiActions.launchEditMoment(any[Bundle]) returns serviceRight(Unit)\n      navigationJobs.launchEditMoment(moment.momentType.name).mustRightUnit\n      there was one(mockNavigationUiActions).launchEditMoment(any)\n    }\n  }\n\n  \"launchWidgets\" should {\n    \"return a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockNavigationUiActions.launchWidgets(any[Bundle]) returns serviceRight(Unit)\n      navigationJobs.launchWidgets().mustRightUnit\n      there was one(mockNavigationUiActions).launchWidgets(any)\n    }\n  }\n\n  \"goToCollection\" should {\n    \"returns a valid response when has a collection \" in new NavigationJobsScope {\n\n      mockTrackEventProcess.useNavigationBar() returns serviceRight(Unit)\n      mockNavigationUiActions.goToCollection(any, any) returns serviceRight(Unit)\n\n      navigationJobs.goToCollection(Option(collection), mockPoint)\n\n      there was one(mockTrackEventProcess).useNavigationBar()\n      there was one(mockNavigationUiActions).goToCollection(collection, mockPoint)\n      there was no(mockNavigationUiActions).showContactUsError()\n    }.pendingUntilFixed\n\n    \"show a error message of contact when hasn't a collection \" in new NavigationJobsScope {\n\n      mockTrackEventProcess.useNavigationBar() returns serviceRight(Unit)\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n\n      navigationJobs.goToCollection(None, mockPoint)\n\n      there was one(mockTrackEventProcess).useNavigationBar()\n      there was one(mockNavigationUiActions).showContactUsError()\n    }.pendingUntilFixed\n  }\n\n  \"openApp\" should {\n    \"returns a valid response when drawer is open\" in new NavigationJobsScope {\n\n      mockLauncherDOM.isDrawerTabsOpened returns true\n      mockAppDrawerUiActions.closeTabs() returns serviceRight(Unit)\n\n      navigationJobs.openApp(applicationData).mustRightUnit\n\n      there was one(mockLauncherDOM).isDrawerTabsOpened\n      there was one(mockAppDrawerUiActions).closeTabs()\n    }\n\n    \"returns a valid response when drawer is close\" in new NavigationJobsScope {\n\n      mockLauncherDOM.isDrawerTabsOpened returns false\n      mockTrackEventProcess.openAppFromAppDrawer(any, any) returns serviceRight(Unit)\n      mockLauncherExecutorProcess.execute(any)(any) returns serviceRight(Unit)\n\n      navigationJobs.openApp(applicationData).mustRightUnit\n\n      there was one(mockLauncherDOM).isDrawerTabsOpened\n      there was no(mockAppDrawerUiActions).closeTabs()\n      there was one(mockTrackEventProcess)\n        .openAppFromAppDrawer(applicationData.packageName, AppCategory(applicationData.category))\n      there was one(mockLauncherExecutorProcess).execute(===(toNineCardIntent(applicationData)))(\n        any)\n    }\n  }\n\n  \"openContact\" should {\n    \"returns a valid response when drawer is open and must be closed\" in new NavigationJobsScope {\n\n      mockLauncherDOM.isDrawerTabsOpened returns true\n      mockAppDrawerUiActions.closeTabs() returns serviceRight(Unit)\n\n      navigationJobs.openContact(contact).mustRightUnit\n\n      there was one(mockLauncherDOM).isDrawerTabsOpened\n      there was one(mockAppDrawerUiActions).closeTabs()\n    }\n\n    \"returns a valid response when drawer is close\" in new NavigationJobsScope {\n\n      mockLauncherDOM.isDrawerTabsOpened returns false\n      mockLauncherExecutorProcess.executeContact(any)(any) returns serviceRight(Unit)\n\n      navigationJobs.openContact(contact).mustRightUnit\n\n      there was one(mockLauncherDOM).isDrawerTabsOpened\n      there was no(mockAppDrawerUiActions).closeTabs()\n      there was one(mockLauncherExecutorProcess).executeContact(===(contact.lookupKey))(any)\n    }\n  }\n\n  \"openLastCall\" should {\n    \"returns a valid response when drawer is open and must be closed\" in new NavigationJobsScope {\n\n      mockLauncherDOM.isDrawerTabsOpened returns true\n      mockAppDrawerUiActions.closeTabs() returns serviceRight(Unit)\n\n      navigationJobs.openLastCall(numberPhone).mustRightUnit\n\n      there was one(mockLauncherDOM).isDrawerTabsOpened\n      there was one(mockAppDrawerUiActions).closeTabs()\n    }\n\n    \"returns a valid response when drawer is close\" in new NavigationJobsScope {\n\n      mockLauncherDOM.isDrawerTabsOpened returns false\n      mockLauncherExecutorProcess.execute(any)(any) returns serviceRight(Unit)\n\n      navigationJobs.openLastCall(numberPhone).mustRightUnit\n\n      there was one(mockLauncherDOM).isDrawerTabsOpened\n      there was no(mockAppDrawerUiActions).closeTabs()\n    }\n  }\n\n  \"openMomentIntent\" should {\n    \"returns a valid response when card has a packageName and moment\" in new NavigationJobsScope {\n\n      mockTrackEventProcess.openAppFromCollection(any, any) returns serviceRight(Unit)\n      mockTrackEventProcess.openApplicationByMoment(any) returns serviceRight(Unit)\n      mockMenuDrawersUiActions.closeAppsMoment() returns serviceRight(Unit)\n      mockLauncherExecutorProcess.execute(any)(any) returns serviceRight(Unit)\n\n      navigationJobs.openMomentIntent(card, Option(NineCardsMoment.defaultMoment)).mustRightUnit\n\n      there was one(mockTrackEventProcess).openAppFromCollection(\n        card.packageName.getOrElse(\"\"),\n        MomentCategory(NineCardsMoment.defaultMoment))\n      there was one(mockTrackEventProcess).openApplicationByMoment(\n        NineCardsMoment.defaultMoment.name)\n      there was one(mockMenuDrawersUiActions).closeAppsMoment()\n    }\n\n    \"returns a valid response when card has a packageName and hasn't moment\" in new NavigationJobsScope {\n\n      mockTrackEventProcess.openAppFromCollection(any, any) returns serviceRight(Unit)\n      mockTrackEventProcess.openApplicationByMoment(any) returns serviceRight(Unit)\n      mockMenuDrawersUiActions.closeAppsMoment() returns serviceRight(Unit)\n      mockLauncherExecutorProcess.execute(any)(any) returns serviceRight(Unit)\n\n      navigationJobs.openMomentIntent(card, None).mustRightUnit\n\n      there was no(mockTrackEventProcess)\n        .openAppFromCollection(card.packageName.getOrElse(\"\"), MomentCategory(moment.momentType))\n      there was no(mockTrackEventProcess).openApplicationByMoment(moment.momentType.name)\n      there was one(mockMenuDrawersUiActions).closeAppsMoment()\n    }\n\n    \"returns a valid response when card hasn't a packageName\" in new NavigationJobsScope {\n\n      mockMenuDrawersUiActions.closeAppsMoment() returns serviceRight(Unit)\n      mockLauncherExecutorProcess.execute(any)(any) returns serviceRight(Unit)\n\n      navigationJobs\n        .openMomentIntent(card.copy(packageName = None), Option(NineCardsMoment.defaultMoment))\n        .mustRightUnit\n\n      there was no(mockTrackEventProcess).openAppFromAppDrawer(any, any)\n      there was one(mockMenuDrawersUiActions).closeAppsMoment()\n    }\n  }\n\n  sequential\n  \"openMomentIntentException\" should {\n    \"returns a valid response when has a number phone\" in new NavigationJobsScope {\n\n      mockUserAccountsProcess.requestPermission(any, any)(any) returns serviceRight(Unit)\n\n      navigationJobs.openMomentIntentException(Option(numberPhone)).mustRightUnit\n\n      there was one(mockUserAccountsProcess)\n        .requestPermission(===(RequestCodes.phoneCallPermission), ===(CallPhone))(any)\n      statuses.lastPhone shouldEqual Option(numberPhone)\n    }\n\n    \"returns a valid response when hasn't a number phone\" in new NavigationJobsScope {\n\n      mockUserAccountsProcess.requestPermission(any, any)(any) returns serviceRight(Unit)\n\n      navigationJobs.openMomentIntentException(None).mustRightUnit\n\n      there was one(mockUserAccountsProcess)\n        .requestPermission(===(RequestCodes.phoneCallPermission), ===(CallPhone))(any)\n      statuses.lastPhone shouldEqual None\n    }\n\n  }\n\n  \"openDockApp\" should {\n    \"returns a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockTrackEventProcess.openDockAppTitle(any) returns serviceRight(Unit)\n      mockTrackEventProcess.openDockAppOrder(any) returns serviceRight(Unit)\n      mockLauncherExecutorProcess.execute(any)(any) returns serviceRight(Unit)\n\n      navigationJobs.openDockApp(dockAppData).mustRightUnit\n\n      there was one(mockTrackEventProcess).openDockAppTitle(dockAppData.name)\n      there was one(mockTrackEventProcess).openDockAppOrder(dockAppData.position)\n      there was one(mockLauncherExecutorProcess).execute(===(dockAppData.intent))(any)\n    }\n  }\n\n  \"launchSearch\" should {\n    \"returns a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockTrackEventProcess.usingSearchByKeyboard() returns serviceRight(Unit)\n      mockLauncherExecutorProcess.launchSearch(any) returns serviceRight(Unit)\n\n      navigationJobs.launchSearch.mustRightUnit\n\n      there was one(mockTrackEventProcess).usingSearchByKeyboard()\n      there was one(mockLauncherExecutorProcess).launchSearch(any)\n    }\n  }\n\n  \"launchVoiceSearch\" should {\n    \"returns a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockTrackEventProcess.usingSearchByVoice() returns serviceRight(Unit)\n      mockLauncherExecutorProcess.launchVoiceSearch(any) returns serviceRight(Unit)\n\n      navigationJobs.launchVoiceSearch.mustRightUnit\n\n      there was one(mockTrackEventProcess).usingSearchByVoice()\n      there was one(mockLauncherExecutorProcess).launchVoiceSearch(any)\n    }\n  }\n\n  \"launchGooglePlay\" should {\n    \"returns a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockLauncherExecutorProcess.launchGooglePlay(any)(any) returns serviceRight(Unit)\n      navigationJobs.launchGooglePlay(packageName).mustRightUnit\n      there was one(mockLauncherExecutorProcess).launchGooglePlay(===(packageName))(any)\n    }\n  }\n\n  \"launchGoogleWeather\" should {\n    \"return false if the service return PermissionDenied for the specified permission\" in new NavigationJobsScope {\n\n      mockTrackEventProcess.goToWeather() returns serviceRight(Unit)\n      mockUserAccountsProcess.havePermission(any)(any) returns serviceRight(\n        PermissionResult(FineLocation, result = false))\n      mockUserAccountsProcess.requestPermission(any, any)(any) returns serviceRight(Unit)\n\n      navigationJobs.launchGoogleWeather().mustRightUnit\n\n      there was one(mockTrackEventProcess).goToWeather()\n      there was one(mockUserAccountsProcess).havePermission(===(FineLocation))(any)\n      there was one(mockUserAccountsProcess)\n        .requestPermission(===(RequestCodes.locationPermission), ===(FineLocation))(any)\n      there was no(mockLauncherExecutorProcess).launchGoogleWeather(any)\n    }\n\n    \"return true if the service return true for the specified permission\" in new NavigationJobsScope {\n\n      mockTrackEventProcess.goToWeather() returns serviceRight(Unit)\n      mockUserAccountsProcess.havePermission(any)(any) returns serviceRight(\n        PermissionResult(FineLocation, result = true))\n      mockLauncherExecutorProcess.launchGoogleWeather(any) returns serviceRight(Unit)\n\n      navigationJobs.launchGoogleWeather().mustRightUnit\n\n      there was one(mockTrackEventProcess).goToWeather()\n      there was one(mockUserAccountsProcess).havePermission(===(FineLocation))(any)\n      there was no(mockUserAccountsProcess)\n        .requestPermission(===(RequestCodes.locationPermission), ===(FineLocation))(any)\n      there was one(mockLauncherExecutorProcess).launchGoogleWeather(any)\n    }\n  }\n\n  \"launchPlayStore\" should {\n    \"returns a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockLauncherExecutorProcess.launchPlayStore(any) returns serviceRight(Unit)\n      navigationJobs.launchPlayStore().mustRightUnit\n      there was one(mockLauncherExecutorProcess).launchPlayStore(any)\n    }\n  }\n\n  \"launchDial\" should {\n    \"returns a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockLauncherExecutorProcess.launchDial(any)(any) returns serviceRight(Unit)\n      navigationJobs.launchDial().mustRightUnit\n      there was one(mockLauncherExecutorProcess).launchDial(===(None))(any)\n    }\n  }\n\n  \"goToChangeMoment\" should {\n    \"returns a valid response when the service returns a right response\" in new NavigationJobsScope {\n\n      mockMomentProcess.getMoments returns serviceRight(seqMoment)\n      mockNavigationUiActions.showSelectMomentDialog(any) returns serviceRight(Unit)\n\n      navigationJobs.goToChangeMoment().mustRightUnit\n\n      there was one(mockMomentProcess).getMoments\n      there was one(mockNavigationUiActions).showSelectMomentDialog(seqMoment)\n    }\n  }\n\n  \"goToMenuOption\" should {\n    \"returns a valid response when itemId is collections \" in new NavigationJobsScope {\n\n      mockTrackEventProcess.goToCollectionsByMenu() returns serviceRight(Unit)\n      mockNavigationUiActions.goToCollectionWorkspace() returns serviceRight(Unit)\n\n      navigationJobs.goToMenuOption(R.id.menu_collections).mustRightUnit\n\n      there was one(mockTrackEventProcess).goToCollectionsByMenu()\n      there was one(mockNavigationUiActions).goToCollectionWorkspace()\n    }\n    \"returns a valid response when itemId is moments \" in new NavigationJobsScope {\n\n      mockTrackEventProcess.goToMomentsByMenu() returns serviceRight(Unit)\n      mockNavigationUiActions.goToMomentWorkspace() returns serviceRight(Unit)\n\n      navigationJobs.goToMenuOption(R.id.menu_moments).mustRightUnit\n\n      there was one(mockTrackEventProcess).goToMomentsByMenu()\n      there was one(mockNavigationUiActions).goToMomentWorkspace()\n    }\n    \"returns a valid response when itemId is profile \" in new NavigationJobsScope {\n\n      mockTrackEventProcess.goToProfileByMenu() returns serviceRight(Unit)\n      mockNavigationUiActions.goToProfile() returns serviceRight(Unit)\n\n      navigationJobs.goToMenuOption(R.id.menu_profile).mustRightUnit\n\n      there was one(mockTrackEventProcess).goToProfileByMenu()\n      there was one(mockNavigationUiActions).goToProfile()\n    }\n    \"shows a wallpaper when itemId is wallpaper \" in new NavigationJobsScope {\n\n      mockNavigationUiActions.launchWallpaper() returns serviceRight(Unit)\n\n      navigationJobs.goToMenuOption(R.id.menu_wallpaper).mustRightUnit\n\n      there was one(mockNavigationUiActions).launchWallpaper()\n    }\n    \"shows settings when itemId is setting \" in new NavigationJobsScope {\n\n      mockNavigationUiActions.launchSettings() returns serviceRight(Unit)\n\n      navigationJobs.goToMenuOption(R.id.menu_settings).mustRightUnit\n\n      there was one(mockNavigationUiActions).launchSettings()\n    }\n    \"shows settings when itemId is widgets \" in new NavigationJobsScope {\n\n      mockNavigationUiActions.launchWidgets(any[Bundle]) returns serviceRight(Unit)\n\n      navigationJobs.goToMenuOption(R.id.menu_widget).mustRightUnit\n\n      there was one(mockNavigationUiActions).launchWidgets(any)\n    }\n    \"returns a Unit when itemId is other \" in new NavigationJobsScope {\n      navigationJobs.goToMenuOption(errorMenu).mustRightUnit\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/launcher/jobs/WidgetJobsSpecification.scala",
    "content": "package cards.nine.app.ui.launcher.jobs\n\nimport android.content.ComponentName\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.ops.WidgetsOps.Cell\nimport cards.nine.app.ui.components.models.{LauncherData, LauncherMoment, MomentWorkSpace}\nimport cards.nine.app.ui.launcher._\nimport cards.nine.app.ui.launcher.LauncherActivity._\nimport cards.nine.app.ui.launcher.jobs.uiactions.{\n  LauncherDOM,\n  NavigationUiActions,\n  WidgetUiActions\n}\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.{AppWidgetTestData, MomentTestData, WidgetTestData}\nimport cards.nine.models.types.{MomentCategory, NineCardsMoment}\nimport cards.nine.process.moment.MomentProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport cards.nine.process.widget.WidgetProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait WidgetJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait WidgetJobsScope\n      extends Scope\n      with MomentTestData\n      with WidgetTestData\n      with AppWidgetTestData\n      with LauncherTestData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    val mockLauncherDOM = mock[LauncherDOM]\n\n    val mockWidgetUiActions = mock[WidgetUiActions]\n    mockWidgetUiActions.dom returns mockLauncherDOM\n\n    val mockNavigationUiActions = mock[NavigationUiActions]\n\n    val mockInjector = mock[Injector]\n\n    val mockWidgetProcess = mock[WidgetProcess]\n\n    mockInjector.widgetsProcess returns mockWidgetProcess\n\n    val mockMomentProcess = mock[MomentProcess]\n\n    mockInjector.momentProcess returns mockMomentProcess\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val widgetsJobs =\n      new WidgetsJobs(mockWidgetUiActions, mockNavigationUiActions)(contextWrapper) {\n\n        override lazy val di: Injector = mockInjector\n\n      }\n\n  }\n\n}\n\nclass WidgetJobsSpec extends WidgetJobsSpecification {\n\n  sequential\n  \"showDialogForDeletingWidget\" should {\n    \"show an error message of contact when hasn't idWidget\" in new WidgetJobsScope {\n\n      statuses = statuses.copy(idWidget = None)\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n      widgetsJobs.showDialogForDeletingWidget(statuses.idWidget).mustRightUnit\n\n      there was no(mockNavigationUiActions).deleteSelectedWidget(any)\n      there was one(mockNavigationUiActions).showContactUsError()\n\n    }\n\n    \"return an Answers when has idWidget\" in new WidgetJobsScope {\n\n      statuses = statuses.copy(idWidget = Option(idWidget))\n      mockNavigationUiActions.deleteSelectedWidget(idWidget) returns serviceRight(Unit)\n      widgetsJobs.showDialogForDeletingWidget(statuses.idWidget).mustRightUnit\n\n      there was one(mockNavigationUiActions).deleteSelectedWidget(idWidget)\n      there was no(mockNavigationUiActions).showContactUsError()\n\n    }\n  }\n  sequential\n  \"deleteWidget\" should {\n\n    \"return an Answers when has idWidget\" in new WidgetJobsScope {\n\n      statuses = statuses.copy(idWidget = Option(idWidget))\n      mockWidgetProcess.deleteWidget(idWidget) returns serviceRight(Unit)\n      mockWidgetProcess.updateWidgets(any) returns serviceRight(Unit)\n      mockWidgetUiActions.closeModeEditWidgets() returns serviceRight(Unit)\n      mockWidgetUiActions.unhostWidget(any) returns serviceRight(Unit)\n\n      widgetsJobs.deleteWidget(idWidget).mustRightUnit\n\n      there was no(mockNavigationUiActions).showContactUsError()\n      there was one(mockWidgetUiActions).closeModeEditWidgets()\n      there was one(mockWidgetUiActions).unhostWidget(idWidget)\n      there was one(mockWidgetProcess).deleteWidget(idWidget)\n    }\n  }\n\n  sequential\n  \"loadWidgetsForMoment\" should {\n    \"returns an Answers when there widget for moments\" in new WidgetJobsScope {\n\n      mockWidgetUiActions.clearWidgets() returns serviceRight(Unit)\n      mockMomentProcess.getMomentByType(any) returns serviceRight(\n        moment.copy(momentType = NineCardsMoment.defaultMoment))\n      mockWidgetProcess.getWidgetsByMoment(any) returns serviceRight(\n        seqWidget map (_.copy(momentId = moment.id)))\n      mockWidgetUiActions.addWidgets(any) returns serviceRight(Unit)\n\n      widgetsJobs.loadWidgetsForMoment(NineCardsMoment.defaultMoment).mustRightUnit\n\n      there was one(mockWidgetUiActions).clearWidgets()\n      there was one(mockMomentProcess).getMomentByType(NineCardsMoment.defaultMoment)\n      there was one(mockWidgetProcess).getWidgetsByMoment(moment.id)\n      there was one(mockWidgetUiActions).addWidgets(seqWidget)\n\n    }\n    \"returns an Answers when not there widget for moments\" in new WidgetJobsScope {\n\n      mockWidgetUiActions.clearWidgets() returns serviceRight(Unit)\n      mockMomentProcess.getMomentByType(any) returns serviceRight(\n        moment.copy(momentType = NineCardsMoment.defaultMoment))\n      mockWidgetProcess.getWidgetsByMoment(any) returns serviceRight(Seq.empty)\n\n      widgetsJobs.loadWidgetsForMoment(NineCardsMoment.defaultMoment).mustRightUnit\n\n      there was one(mockWidgetUiActions).clearWidgets()\n      there was one(mockMomentProcess).getMomentByType(NineCardsMoment.defaultMoment)\n      there was one(mockWidgetProcess).getWidgetsByMoment(moment.id)\n      there was no(mockWidgetUiActions).addWidgets(any)\n\n    }\n  }\n\n  sequential\n  \"addWidget\" should {\n    \"show an error message of contact when parameter is None\" in new WidgetJobsScope {\n\n      mockTrackEventProcess.addWidget(any) returns serviceRight(Unit)\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n\n      widgetsJobs.addWidget(None).mustRightUnit\n\n      there was no(mockTrackEventProcess).addWidget(===(widget.packageName))\n      there was one(mockNavigationUiActions).showContactUsError()\n\n    }\n\n    \"show an error message of contact when hasn't data in LauncherDOM\" in new WidgetJobsScope {\n\n      mockTrackEventProcess.addWidget(any) returns serviceRight(Unit)\n      mockLauncherDOM.getData returns Seq.empty\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n\n      widgetsJobs.addWidget(Option(appWidgetId)).mustRightUnit\n\n      there was no(mockTrackEventProcess).addWidget(===(widget.packageName))\n      there was one(mockNavigationUiActions).showContactUsError()\n    }\n\n    \"show an error message of contact when hasn't a moment \" in new WidgetJobsScope {\n\n      mockTrackEventProcess.addWidget(any) returns serviceRight(Unit)\n      mockLauncherDOM.getData returns Seq(launcherData.copy(moment = None))\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n\n      widgetsJobs.addWidget(Option(appWidgetId)).mustRightUnit\n\n      there was no(mockTrackEventProcess).addWidget(===(widget.packageName))\n      there was one(mockNavigationUiActions).showContactUsError()\n\n    }\n\n    \"show an error message of contact when hasn't a momentType \" in new WidgetJobsScope {\n\n      mockTrackEventProcess.addWidget(any) returns serviceRight(Unit)\n      mockLauncherDOM.getData returns Seq(\n        launcherData.copy(moment = Option(launcherMoment.copy(momentType = None))))\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n\n      widgetsJobs.addWidget(Option(appWidgetId)).mustRightUnit\n\n      there was no(mockTrackEventProcess).addWidget(===(widget.packageName))\n      there was one(mockNavigationUiActions).showContactUsError()\n\n    }\n\n    \"return a valid response when statuses hasn't widget\" in new WidgetJobsScope {\n\n      val provider = new ComponentName(widget.packageName, widget.className)\n      val cell     = new Cell(spanX = 1, spanY = 1, widthCell = 1, heightCell = 1)\n\n      mockLauncherDOM.getData returns Seq(launcherData)\n      statuses = statuses.copy(hostingNoConfiguredWidget = None)\n\n      mockTrackEventProcess.addWidget(any) returns serviceRight(Unit)\n      mockMomentProcess.getMomentByType(any) returns serviceRight(\n        moment.copy(momentType = NineCardsMoment.defaultMoment))\n      mockWidgetUiActions.getWidgetInfoById(any) returns serviceRight(Option((provider, cell)))\n      mockWidgetProcess.getWidgetsByMoment(any) returns serviceRight(Seq(widget))\n\n      mockWidgetProcess.addWidget(any) returns serviceRight(widget)\n      mockWidgetUiActions.addWidgets(any) returns serviceRight(Unit)\n\n      widgetsJobs.addWidget(Option(appWidgetId)).mustRightUnit\n\n      there was one(mockTrackEventProcess).addWidget(===(widget.packageName))\n      there was no(mockNavigationUiActions).showContactUsError()\n      there was one(mockMomentProcess).getMomentByType(NineCardsMoment.defaultMoment)\n    }\n\n    \"return a valid response when statuses has widget \" in new WidgetJobsScope {\n\n      mockTrackEventProcess.addWidget(any) returns serviceRight(Unit)\n      mockLauncherDOM.getData returns Seq(launcherData)\n      statuses = statuses.copy(hostingNoConfiguredWidget = Option(widget))\n      mockWidgetProcess.updateAppWidgetId(any, any) returns serviceRight(widget)\n      mockWidgetUiActions.replaceWidget(any) returns serviceRight(Unit)\n\n      mockWidgetProcess.addWidgets(any) returns serviceRight(seqWidget)\n\n      widgetsJobs.addWidget(Option(appWidgetId)).mustRightUnit\n\n      there was no(mockTrackEventProcess).addWidget(===(widget.packageName))\n      there was no(mockNavigationUiActions).showContactUsError()\n\n    }\n  }\n\n  sequential\n  \"hostNoConfiguredWidget\" should {\n    \"return a valid response when the service returns a right response\" in new WidgetJobsScope {\n\n      mockWidgetUiActions.hostWidget(any, any) returns serviceRight(Unit)\n      widgetsJobs.hostNoConfiguredWidget(widget).mustRightUnit\n      there was one(mockWidgetUiActions).hostWidget(widget.packageName, widget.className)\n    }\n\n  }\n\n  sequential\n  \"hostWidget\" should {\n    \"return a valid response when the service returns a right response\" in new WidgetJobsScope {\n\n      mockLauncherDOM.getData returns Seq(launcherData)\n      mockTrackEventProcess.addWidgetToMoment(any, any, any) returns serviceRight(Unit)\n      mockWidgetUiActions.hostWidget(any, any) returns serviceRight(Unit)\n\n      widgetsJobs.hostWidget(appWidget).mustRightUnit\n\n      there was one(mockTrackEventProcess).addWidgetToMoment(\n        appWidget.packageName,\n        appWidget.className,\n        MomentCategory(NineCardsMoment.defaultMoment))\n      there was one(mockWidgetUiActions).hostWidget(appWidget.packageName, appWidget.className)\n    }\n\n    \"return a valid response when the LauncherDOM returns a Seq.empty\" in new WidgetJobsScope {\n\n      mockLauncherDOM.getData returns Seq.empty\n      mockWidgetUiActions.hostWidget(any, any) returns serviceRight(Unit)\n\n      widgetsJobs.hostWidget(appWidget).mustRightUnit\n\n      there was no(mockTrackEventProcess).addWidgetToMoment(any, any, any)\n      there was one(mockWidgetUiActions).hostWidget(appWidget.packageName, appWidget.className)\n\n    }\n  }\n\n  sequential\n  \"configureOrAddWidget\" should {\n    \"return a valid response when has AppWidgetId\" in new WidgetJobsScope {\n      mockWidgetUiActions.configureWidget(any) returns serviceRight(Unit)\n\n      widgetsJobs.configureOrAddWidget(Option(appWidgetId)).mustRightUnit\n\n      there was one(mockWidgetUiActions).configureWidget(appWidgetId)\n\n    }\n\n    \"show an error message of contact when hasn't AppWidgetId\" in new WidgetJobsScope {\n\n      mockNavigationUiActions.showContactUsError() returns serviceRight(Unit)\n\n      widgetsJobs.configureOrAddWidget(None).mustRightUnit\n\n      there was no(mockWidgetUiActions).configureWidget(any)\n      there was one(mockNavigationUiActions).showContactUsError()\n\n    }\n  }\n\n  sequential\n  \"openModeEditWidgets\" should {\n    \"return a valid response when hasn't workspaceScrolling\" in new WidgetJobsScope {\n\n      mockLauncherDOM.isWorkspaceScrolling returns false\n      mockWidgetUiActions.openModeEditWidgets() returns serviceRight(Unit)\n\n      widgetsJobs.openModeEditWidgets(idWidget).mustRightUnit\n\n      statuses.mode shouldEqual EditWidgetsMode\n      statuses.transformation shouldEqual None\n      there was one(mockWidgetUiActions).openModeEditWidgets()\n    }\n\n    \"return a valid response when has workspaceScrolling\" in new WidgetJobsScope {\n\n      mockLauncherDOM.isWorkspaceScrolling returns true\n      widgetsJobs.openModeEditWidgets(idWidget).mustRightUnit\n      there was no(mockWidgetUiActions).openModeEditWidgets()\n    }\n  }\n\n  sequential\n  \"backToActionEditWidgets\" should {\n    \"return a valid response when the service returns a right response\" in new WidgetJobsScope {\n\n      mockWidgetUiActions.reloadViewEditWidgets() returns serviceRight(Unit)\n      widgetsJobs.backToActionEditWidgets().mustRightUnit\n\n      statuses.transformation shouldEqual None\n      there was one(mockWidgetUiActions).reloadViewEditWidgets()\n    }\n\n  }\n\n  sequential\n  \"closeModeEditWidgets\" should {\n    \"return a valid response when the service returns a right response\" in new WidgetJobsScope {\n\n      mockWidgetUiActions.closeModeEditWidgets() returns serviceRight(Unit)\n      mockWidgetProcess.updateWidgets(any) returns serviceRight(Unit)\n\n      widgetsJobs.closeModeEditWidgets().mustRightUnit\n\n      statuses.idWidget shouldEqual None\n      statuses.mode shouldEqual NormalMode\n      there was one(mockWidgetUiActions).closeModeEditWidgets()\n    }\n  }\n\n  sequential\n  \"cancelWidget\" should {\n    \"returns an Answers when statuses.mode equal EditWigetsMode\" in new WidgetJobsScope {\n\n      statuses = statuses.copy(mode = EditWidgetsMode)\n      mockWidgetUiActions.cancelWidget(any) returns serviceRight(Unit)\n\n      widgetsJobs.cancelWidget(Option(appWidgetId)).mustRightUnit\n\n      there was one(mockWidgetUiActions).cancelWidget(appWidgetId)\n    }\n\n    \"returns an Answers when statuses.mode not equal EditWigetsMode \" in new WidgetJobsScope {\n\n      statuses = statuses.copy(mode = ReorderMode)\n      widgetsJobs.cancelWidget(Option(appWidgetId)).mustRightUnit\n      there was no(mockWidgetUiActions).cancelWidget(appWidgetId)\n    }\n\n    \"returns an Answers when hasn't appwidgetId\" in new WidgetJobsScope {\n\n      statuses = statuses.copy(mode = EditWidgetsMode)\n      widgetsJobs.cancelWidget(None).mustRightUnit\n      there was no(mockWidgetUiActions).cancelWidget(any)\n    }\n  }\n\n  \"editWidgetsShowActions\" should {\n    \"return a valid response when the service returns a right response\" in new WidgetJobsScope {\n\n      mockWidgetUiActions.editWidgetsShowActions() returns serviceRight(Unit)\n      widgetsJobs.editWidgetsShowActions().mustRightUnit\n      there was one(mockWidgetUiActions).editWidgetsShowActions()\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/profile/jobs/ProfileJobsSpec.scala",
    "content": "package cards.nine.app.ui.profile.jobs\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.content.res.Resources\nimport android.support.v7.app.AppCompatActivity\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.{BroadAction, JobException, RequestCodes}\nimport cards.nine.app.ui.launcher.jobs.LauncherTestData\nimport cards.nine.app.ui.profile.ProfileActivity._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.CloudStorageValues._\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.commons.test.data.{CloudStorageTestData, SharedCollectionTestData, UserTestData}\nimport cards.nine.models.RawCloudStorageDevice\nimport cards.nine.models.types.{PublishedByMe, PublishedByOther}\nimport cards.nine.process.cloud.CloudStorageProcess\nimport cards.nine.process.cloud.impl.CloudStorageProcessImplData\nimport cards.nine.process.collection.{CollectionException, CollectionProcess}\nimport cards.nine.process.device.{AppException, DeviceProcess}\nimport cards.nine.process.intents.LauncherExecutorProcess\nimport cards.nine.process.moment.MomentProcess\nimport cards.nine.process.sharedcollections.SharedCollectionsProcess\nimport cards.nine.process.theme.ThemeProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport cards.nine.process.user.{UserException, UserProcess}\nimport cards.nine.process.widget.WidgetProcess\nimport com.google.android.gms.common.api.GoogleApiClient\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait ProfileJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait ProfileJobsScope\n      extends Scope\n      with LauncherTestData\n      with UserTestData\n      with SharedCollectionTestData\n      with CloudStorageTestData\n      with CloudStorageProcessImplData {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    implicit val contextSupport = mock[ContextSupport]\n\n    val mockResources = mock[Resources]\n\n    contextSupport.getResources returns mockResources\n\n    val mockInjector = mock[Injector]\n\n    val mockProfileUiActions = mock[ProfileUiActions]\n\n    val mockApiClient = mock[GoogleApiClient]\n\n    val mockThemeProcess = mock[ThemeProcess]\n\n    mockInjector.themeProcess returns mockThemeProcess\n\n    val mockUserProcess = mock[UserProcess]\n\n    mockInjector.userProcess returns mockUserProcess\n\n    val mockCloudStorageProcess = mock[CloudStorageProcess]\n\n    mockInjector.cloudStorageProcess returns mockCloudStorageProcess\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val mockCollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val mockLauncherExecutorProcess = mock[LauncherExecutorProcess]\n\n    mockInjector.launcherExecutorProcess returns mockLauncherExecutorProcess\n\n    val mockSharedCollectionsProcess = mock[SharedCollectionsProcess]\n\n    mockInjector.sharedCollectionsProcess returns mockSharedCollectionsProcess\n\n    val mockMomentProcess = mock[MomentProcess]\n\n    mockInjector.momentProcess returns mockMomentProcess\n\n    val mockWidgetProcess = mock[WidgetProcess]\n\n    mockInjector.widgetsProcess returns mockWidgetProcess\n\n    val mockIntent = mock[Intent]\n\n    val profileJobs = new ProfileJobs(mockProfileUiActions)(contextWrapper) {\n\n      override lazy val di: Injector = mockInjector\n\n      override def getThemeTask = TaskService.right(theme)\n\n      override def themeFile = \"\"\n\n      override def sendBroadCastTask(broadAction: BroadAction) = TaskService.empty\n\n      override def withActivityTask(f: (AppCompatActivity => Unit)) = TaskService.empty\n\n      override def getString(res: Int): String = \"\"\n\n      override def getString(res: Int, args: AnyRef*): String = \"\"\n\n    }\n\n  }\n\n}\n\nclass ProfileJobsSpec extends ProfileJobsSpecification {\n\n  sequential\n  \"driveConnected\" should {\n\n    \"returns a valid response when apiclient is connected\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = Option(mockApiClient))\n      mockApiClient.isConnected returns true\n\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n      mockCloudStorageProcess.getCloudStorageDevices(any)(any) returns serviceRight(Seq.empty)\n      mockProfileUiActions.showEmptyAccountsContent(any) returns serviceRight(Unit)\n      mockTrackEventProcess.showAccountsContent() returns serviceRight(Unit)\n\n      profileJobs.driveConnected().mustRightUnit\n\n      there was one(mockProfileUiActions).showLoading()\n    }\n\n    \"returns a valid response when apiclient isn't connected\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = Option(mockApiClient))\n      mockApiClient.isConnected returns false\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n\n      profileJobs.driveConnected().mustRightUnit\n\n      there was one(mockProfileUiActions).showLoading()\n    }\n\n    \"returns a JobException when the connection throws a exception\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = Option(mockApiClient))\n      mockApiClient.isConnected returns false\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n      mockApiClient.connect() throws new RuntimeException(\"\")\n\n      profileJobs.driveConnected().mustLeft[JobException]\n\n      there was one(mockProfileUiActions).showLoading()\n    }\n\n    \"Do nothing when ApiClient is None\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = None)\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n\n      profileJobs.driveConnected().mustRightUnit\n\n      there was one(mockProfileUiActions).showLoading()\n    }\n  }\n\n  \"initialize\" should {\n    \"initialize profile jobs when the service returns a right response\" in new ProfileJobsScope {\n\n      mockThemeProcess.getTheme(any)(any) returns serviceRight(theme)\n      mockProfileUiActions.initialize(any) returns serviceRight(Unit)\n      mockUserProcess.getUser(any) returns serviceRight(user)\n      mockCloudStorageProcess.createCloudStorageClient(any)(any) returns serviceRight(\n        mockApiClient)\n      mockProfileUiActions.userProfile(any, any, any) returns serviceRight(Unit)\n\n      profileJobs.initialize().mustRightUnit\n\n      there was one(mockProfileUiActions).initialize(theme)\n      there was one(mockCloudStorageProcess).createCloudStorageClient(\n        ===(user.email.getOrElse(\"\")))(any)\n      there was one(mockProfileUiActions)\n        .userProfile(user.userProfile.name, user.email.getOrElse(\"\"), user.userProfile.avatar)\n    }\n\n    \"returns a JobException when the user doesn't have email\" in new ProfileJobsScope {\n\n      mockThemeProcess.getTheme(any)(any) returns serviceRight(theme)\n      mockProfileUiActions.initialize(any) returns serviceRight(Unit)\n      mockUserProcess.getUser(any) returns serviceRight(user.copy(email = None))\n\n      profileJobs.initialize().mustLeft[JobException]\n\n      there was one(mockProfileUiActions).initialize(theme)\n    }\n\n    \"returns a JobException when the process returns a exception\" in new ProfileJobsScope {\n\n      mockThemeProcess.getTheme(any)(any) returns serviceRight(theme)\n      mockProfileUiActions.initialize(any) returns serviceRight(Unit)\n      mockUserProcess.getUser(any) returns serviceLeft(UserException(\"\"))\n\n      profileJobs.initialize().mustLeft[UserException]\n\n      there was one(mockProfileUiActions).initialize(theme)\n    }\n  }\n\n  \"resume\" should {\n    \"call to askBroadCastTask and return a valid response when the service returns a right response\" in new ProfileJobsScope {\n\n      override val profileJobs = new ProfileJobs(mockProfileUiActions)(contextWrapper) {\n        override def askBroadCastTask(broadAction: BroadAction) = TaskService.empty\n      }\n\n      profileJobs.resume().mustRightUnit\n    }\n\n    \"returns a JobException when call to askBroadCastTask and return an exception\" in new ProfileJobsScope {\n\n      override val profileJobs = new ProfileJobs(mockProfileUiActions)(contextWrapper) {\n        override def sendBroadCastTask(broadAction: BroadAction) =\n          TaskService.left(JobException(\"\"))\n      }\n\n      profileJobs.resume().mustLeft[JobException]\n    }\n  }\n\n  \"stop\" should {\n    \"returns a valid response when apiclient is disconnect\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = Option(mockApiClient))\n      profileJobs.stop().mustRightUnit\n    }\n\n    \"Do nothing when ApiClient is None\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = None)\n      profileJobs.stop().mustRightUnit\n    }\n\n    \"returns a JobException when the disconnect throws a exception\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = Option(mockApiClient))\n      mockApiClient.disconnect() throws new RuntimeException(\"\")\n      profileJobs.stop().mustLeft[JobException]\n    }\n  }\n\n  \"onOffsetChanged\" should {\n    \"returns a valid response when the actions return a right responses\" in new ProfileJobsScope {\n      val percentage: Float = 1\n\n      mockProfileUiActions.handleToolbarVisibility(any) returns serviceRight(Unit)\n      mockProfileUiActions.handleProfileVisibility(any) returns serviceRight(Unit)\n\n      profileJobs.onOffsetChanged(percentage).mustRightUnit\n\n      there was one(mockProfileUiActions).handleToolbarVisibility(percentage)\n      there was one(mockProfileUiActions).handleProfileVisibility(percentage)\n    }\n  }\n\n  \"accountSynced\" should {\n    \"returns a valid response when the service returns a right response\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.synchronizeConfiguration() returns serviceRight(Unit)\n\n      statuses = statuses.copy(apiClient = Option(mockApiClient))\n      mockApiClient.isConnected returns true\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n      mockCloudStorageProcess.getCloudStorageDevices(any)(any) returns serviceRight(Seq.empty)\n      mockProfileUiActions.showEmptyAccountsContent(any) returns serviceRight(Unit)\n      mockTrackEventProcess.showAccountsContent() returns serviceRight(Unit)\n      mockProfileUiActions.showMessageAccountSynced() returns serviceRight(Unit)\n\n      profileJobs.accountSynced().mustRightUnit\n\n    }\n  }\n\n  \"errorSyncing\" should {\n    \"shows a message syncing error\" in new ProfileJobsScope {\n\n      mockProfileUiActions.showSyncingError() returns serviceRight(Unit)\n\n      profileJobs.errorSyncing().mustRightUnit\n\n      there was one(mockProfileUiActions).showSyncingError()\n      profileJobs.syncEnabled shouldEqual true\n    }\n  }\n\n  \"stateSyncing\" should {\n    \"updated the variable syncEnabled\" in new ProfileJobsScope {\n\n      profileJobs.stateSyncing().mustRightUnit\n      profileJobs.syncEnabled shouldEqual false\n    }\n  }\n\n  \"saveSharedCollection\" should {\n\n    \"returns a valid response and call to subscribe when the process returns a right\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.addToMyCollectionsFromProfile(any) returns serviceRight(Unit)\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n      mockCollectionProcess.addCollection(any) returns serviceRight(collection)\n      mockProfileUiActions.showAddCollectionMessage(any) returns serviceRight(Unit)\n      mockSharedCollectionsProcess.subscribe(any)(any) returns serviceRight(Unit)\n\n      profileJobs\n        .saveSharedCollection(sharedCollection.copy(publicCollectionStatus = PublishedByOther))\n        .mustRightUnit\n\n      there was one(mockTrackEventProcess).addToMyCollectionsFromProfile(sharedCollection.name)\n      there was one(mockProfileUiActions).showAddCollectionMessage(\n        sharedCollection.sharedCollectionId)\n      there was one(mockSharedCollectionsProcess).subscribe(\n        ===(sharedCollection.sharedCollectionId))(any)\n    }\n\n    \"doesn't call to subscribe when the status is PublishedByMe\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.addToMyCollectionsFromProfile(any) returns serviceRight(Unit)\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n      mockCollectionProcess.addCollection(any) returns serviceRight(collection)\n      mockProfileUiActions.showAddCollectionMessage(any) returns serviceRight(Unit)\n      mockSharedCollectionsProcess.subscribe(any)(any) returns serviceRight(Unit)\n\n      profileJobs\n        .saveSharedCollection(sharedCollection.copy(publicCollectionStatus = PublishedByMe))\n        .mustRightUnit\n\n      there was one(mockTrackEventProcess).addToMyCollectionsFromProfile(sharedCollection.name)\n      there was one(mockProfileUiActions).showAddCollectionMessage(\n        sharedCollection.sharedCollectionId)\n      there was no(mockSharedCollectionsProcess).subscribe(any)(any)\n    }\n\n    \"returns an AppException when the process returns an exception\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.addToMyCollectionsFromProfile(any) returns serviceRight(Unit)\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceLeft(AppException(\"\"))\n\n      profileJobs.saveSharedCollection(sharedCollection).mustLeft[AppException]\n\n      there was one(mockTrackEventProcess).addToMyCollectionsFromProfile(sharedCollection.name)\n      there was no(mockProfileUiActions).showAddCollectionMessage(\n        sharedCollection.sharedCollectionId)\n    }\n\n    \"returns an CollectionException when the process returns an exception\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.addToMyCollectionsFromProfile(any) returns serviceRight(Unit)\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n      mockCollectionProcess.addCollection(any) returns serviceLeft(CollectionException(\"\"))\n\n      profileJobs.saveSharedCollection(sharedCollection).mustLeft[CollectionException]\n\n      there was one(mockTrackEventProcess).addToMyCollectionsFromProfile(sharedCollection.name)\n      there was no(mockProfileUiActions).showAddCollectionMessage(\n        sharedCollection.sharedCollectionId)\n    }\n  }\n\n  \"shareCollection\" should {\n    \"call to launchShare\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.shareCollectionFromProfile(any) returns serviceRight(Unit)\n      mockLauncherExecutorProcess.launchShare(any)(any) returns serviceRight(Unit)\n\n      profileJobs.shareCollection(sharedCollection).mustRightUnit\n\n    }\n  }\n\n  \"loadPublications\" should {\n    \"load publications when the service returns a right response\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.showPublicationsContent() returns serviceRight(Unit)\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.getPublishedCollections()(any) returns serviceRight(\n        seqSharedCollection)\n      mockProfileUiActions.loadPublications(any) returns serviceRight(Unit)\n\n      profileJobs.loadPublications().mustRightUnit\n\n      there was one(mockProfileUiActions).loadPublications(seqSharedCollection)\n    }\n\n    \"call to showEmptyPublicationsContent when doesn't have sharecollections.\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.showPublicationsContent() returns serviceRight(Unit)\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.getPublishedCollections()(any) returns serviceRight(Seq.empty)\n      mockProfileUiActions.showEmptyPublicationsContent(any) returns serviceRight(Unit)\n\n      profileJobs.loadPublications().mustRightUnit\n\n      there was one(mockProfileUiActions).showEmptyPublicationsContent(false)\n    }\n  }\n\n  \"loadSubscriptions\" should {\n    \"load subscription when the service returns a right response\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.showSubscriptionsContent() returns serviceRight(Unit)\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.getSubscriptions()(any) returns serviceRight(seqSubscriptions)\n      mockProfileUiActions.setSubscriptionsAdapter(any) returns serviceRight(Unit)\n\n      profileJobs.loadSubscriptions().mustRightUnit\n\n      there was one(mockProfileUiActions).setSubscriptionsAdapter(seqSubscriptions)\n    }\n\n    \"call to showEmptySubscriptionsContent when doesn't have susbcription.\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.showSubscriptionsContent() returns serviceRight(Unit)\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n      mockSharedCollectionsProcess.getSubscriptions()(any) returns serviceRight(Seq.empty)\n      mockProfileUiActions.showEmptySubscriptionsContent(any) returns serviceRight(Unit)\n\n      profileJobs.loadSubscriptions().mustRightUnit\n\n      there was one(mockProfileUiActions).showEmptySubscriptionsContent(false)\n    }\n  }\n\n  \"changeSubscriptionStatus\" should {\n    \"change subscription status to Subscribe\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.subscribeToCollection(any) returns serviceRight(Unit)\n      mockSharedCollectionsProcess.subscribe(any)(any) returns serviceRight(Unit)\n      mockTrackEventProcess.unsubscribeFromCollection(any) returns serviceRight(Unit)\n      mockSharedCollectionsProcess.unsubscribe(any)(any) returns serviceRight(Unit)\n      mockProfileUiActions.showUpdatedSubscriptions(any, any) returns serviceRight(Unit)\n\n      profileJobs.changeSubscriptionStatus(sharedCollection.id, true).mustRightUnit\n\n      there was one(mockProfileUiActions).showUpdatedSubscriptions(sharedCollection.id, true)\n\n    }\n\n    \"change subscription status to Unsubscribe\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.subscribeToCollection(any) returns serviceRight(Unit)\n      mockSharedCollectionsProcess.subscribe(any)(any) returns serviceRight(Unit)\n      mockTrackEventProcess.unsubscribeFromCollection(any) returns serviceRight(Unit)\n      mockSharedCollectionsProcess.unsubscribe(any)(any) returns serviceRight(Unit)\n      mockProfileUiActions.showUpdatedSubscriptions(any, any) returns serviceRight(Unit)\n\n      profileJobs.changeSubscriptionStatus(sharedCollection.id, false).mustRightUnit\n\n      there was one(mockProfileUiActions).showUpdatedSubscriptions(sharedCollection.id, false)\n\n    }\n  }\n\n  \"activityResult\" should {\n    \"returns a JobException when the connection throws a exceptionp\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = Option(mockApiClient))\n      mockApiClient.connect() throws new RuntimeException(\"\")\n\n      profileJobs\n        .activityResult(RequestCodes.resolveGooglePlayConnection, Activity.RESULT_OK, mockIntent)\n        .mustLeft[JobException]\n    }\n\n    \"try connect when the service returns a right response\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = Option(mockApiClient))\n      profileJobs\n        .activityResult(RequestCodes.resolveGooglePlayConnection, Activity.RESULT_OK, mockIntent)\n        .mustRightUnit\n    }\n\n    \"shows an empty accounts\" in new ProfileJobsScope {\n\n      mockProfileUiActions.showEmptyAccountsContent(any) returns serviceRight(Unit)\n      profileJobs\n        .activityResult(RequestCodes.resolveGooglePlayConnection, 2, mockIntent)\n        .mustRightUnit\n      there was one(mockProfileUiActions).showEmptyAccountsContent(true)\n    }\n  }\n\n  \"quit\" should {\n    \"call to all process for delete and clean\" in new ProfileJobsScope {\n\n      mockTrackEventProcess.logout() returns serviceRight(Unit)\n      mockCollectionProcess.cleanCollections() returns serviceRight(Unit)\n      mockDeviceProcess.deleteAllDockApps() returns serviceRight(Unit)\n      mockMomentProcess.deleteAllMoments() returns serviceRight(Unit)\n      mockWidgetProcess.deleteAllWidgets() returns serviceRight(Unit)\n      mockUserProcess.unregister(any) returns serviceRight(Unit)\n\n      profileJobs.quit().mustRightUnit\n\n    }\n  }\n\n  \"launchService\" should {\n    \"launch service when synEnable is true\" in new ProfileJobsScope {\n\n      mockProfileUiActions.showMessageSyncingAccount() returns serviceRight(Unit)\n\n      profileJobs.launchService().mustRightUnit\n      profileJobs.syncEnabled shouldEqual false\n    }\n\n    \"Do nothing if syncEnable is false\" in new ProfileJobsScope {\n\n      mockProfileUiActions.showMessageSyncingAccount() returns serviceRight(Unit)\n\n      profileJobs.syncEnabled = false\n      profileJobs.launchService().mustRightUnit\n    }\n  }\n\n  \"deleteDevice\" should {\n    \"return a valid response when the service returns a right response\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = Option(mockApiClient))\n      mockApiClient.isConnected returns true\n\n      mockTrackEventProcess.showAccountsContent() returns serviceRight(Unit)\n      mockCloudStorageProcess.getCloudStorageDevices(any)(any) returns serviceRight(\n        Seq(cloudStorageDeviceSummary))\n      mockProfileUiActions.showEmptyAccountsContent(any) returns serviceRight(Unit)\n\n      mockTrackEventProcess.deleteConfiguration() returns serviceRight(Unit)\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n      mockCloudStorageProcess.deleteCloudStorageDevice(any, any) returns serviceRight(Unit)\n\n      profileJobs.deleteDevice(cloudId).mustRightUnit\n\n      there was two(mockProfileUiActions).showLoading()\n    }\n\n    \"returns a valid response when the client don't have connection\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = Option(mockApiClient))\n      mockApiClient.isConnected returns false\n\n      mockTrackEventProcess.showAccountsContent() returns serviceRight(Unit)\n      mockCloudStorageProcess.getCloudStorageDevices(any)(===(contextSupport)) returns serviceRight(\n        Seq(cloudStorageDeviceSummary))\n      mockProfileUiActions.showEmptyAccountsContent(any) returns serviceRight(Unit)\n\n      mockTrackEventProcess.deleteConfiguration() returns serviceRight(Unit)\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n      mockCloudStorageProcess.deleteCloudStorageDevice(any, any) returns serviceRight(Unit)\n\n      profileJobs.deleteDevice(deviceCloudId).mustRightUnit\n\n    }\n\n    \"returns a valid response when the user doesn't have email\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = Option(mockApiClient))\n\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n      mockUserProcess.getUser(any) returns serviceRight(user.copy(email = None))\n\n      mockCloudStorageProcess.createCloudStorageClient(any)(any) returns serviceRight(\n        mockApiClient)\n\n      profileJobs.deleteDevice(deviceCloudId).mustRightUnit\n\n    }\n\n    \"return a valid response when the service returns a right response\" in new ProfileJobsScope {\n\n      statuses = statuses.copy(apiClient = None)\n\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n      mockUserProcess.getUser(any) returns serviceRight(user)\n\n      mockCloudStorageProcess.createCloudStorageClient(any)(any) returns serviceRight(\n        mockApiClient)\n\n      profileJobs.deleteDevice(deviceCloudId).mustRightUnit\n\n    }\n  }\n\n  \"printDeviceInfo\" should {\n    \"return a valid response when the service returns a right response\" in new ProfileJobsScope {\n\n      val expected = RawCloudStorageDevice(\n        cloudId = cloudId,\n        uuid = driveServiceFile.summary.uuid,\n        deviceId = driveServiceFile.summary.deviceId,\n        title = driveServiceFile.summary.title,\n        createdDate = driveServiceFile.summary.createdDate,\n        modifiedDate = driveServiceFile.summary.modifiedDate,\n        json = driveServiceFile.content)\n\n      statuses = statuses.copy(apiClient = Option(mockApiClient))\n      mockApiClient.isConnected returns true\n      mockProfileUiActions.showLoading() returns serviceRight(Unit)\n      mockCloudStorageProcess.getRawCloudStorageDevice(any, any) returns serviceRight(expected)\n\n      profileJobs.printDeviceInfo(deviceCloudId).mustRightUnit\n\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/wizard/jobs/LoadConfigurationJobsSpec.scala",
    "content": "package cards.nine.app.ui.wizard.jobs\n\nimport cards.nine.app.commons.Conversions\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.JobException\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.CloudStorageValues._\nimport cards.nine.commons.test.data._\nimport cards.nine.process.cloud.{CloudStorageProcess, CloudStorageProcessException}\nimport cards.nine.process.collection.CollectionProcess\nimport cards.nine.process.device.DeviceProcess\nimport cards.nine.process.moment.MomentProcess\nimport cards.nine.process.thirdparty.ExternalServicesProcess\nimport cards.nine.process.user.UserProcess\nimport com.google.android.gms.common.api.GoogleApiClient\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait LoadConfigurationJobsSpecification\n    extends TaskServiceSpecification\n    with Mockito\n    with ApiTestData\n    with ApplicationTestData\n    with MomentTestData\n    with CloudStorageTestData\n    with CollectionTestData\n    with DockAppTestData\n    with Conversions {\n\n  trait LoadConfigurationJobsScope extends Scope {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    implicit val contextSupport = mock[ContextSupport]\n\n    val cloudStorageProcessException = CloudStorageProcessException(\"\")\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val mockCollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val mockMomentProcess = mock[MomentProcess]\n\n    mockInjector.momentProcess returns mockMomentProcess\n\n    val mockCloudStorageProcess = mock[CloudStorageProcess]\n\n    mockInjector.cloudStorageProcess returns mockCloudStorageProcess\n\n    val mockUserProcess = mock[UserProcess]\n\n    mockInjector.userProcess returns mockUserProcess\n\n    val mockExternalServicesProcess = mock[ExternalServicesProcess]\n\n    mockInjector.externalServicesProcess returns mockExternalServicesProcess\n\n    val mockApiClient = mock[GoogleApiClient]\n\n    val loadConfigurationJobs = new LoadConfigurationJobs() {\n\n      override lazy val di: Injector = mockInjector\n\n    }\n\n  }\n\n}\n\nclass LoadConfigurationJobsSpec extends LoadConfigurationJobsSpecification {\n\n  \"loadConfiguration\" should {\n\n    \"return a valid response when the service returns a right response\" in new LoadConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCloudStorageProcess.getCloudStorageDevice(mockApiClient, cloudId) returns serviceRight(\n        cloudStorageDevice)\n\n      mockExternalServicesProcess.readFirebaseToken returns serviceRight(tokenFirebase)\n\n      mockCollectionProcess.createCollectionsFromCollectionData(any)(any) returns serviceRight(\n        seqCollection)\n      mockMomentProcess.saveMoments(any)(any) returns serviceRight(seqMoment)\n      mockDeviceProcess.saveDockApps(any) returns serviceRight(seqDockApp)\n      mockUserProcess.updateUserDevice(any, any, any)(any) returns serviceRight(Unit)\n\n      loadConfigurationJobs.loadConfiguration(mockApiClient, cloudId).mustRightUnit\n\n      there was one(mockCollectionProcess).createCollectionsFromCollectionData(\n        ===(toSeqCollectionData(cloudStorageDevice.data.collections)))(any)\n      there was one(mockMomentProcess).saveMoments(===(momentSeq getOrElse Seq.empty))(any)\n      there was one(mockDeviceProcess).saveDockApps(===(dockAppSeq getOrElse Seq.empty))\n      there was one(mockUserProcess).updateUserDevice(\n        ===(cloudStorageDevice.data.deviceName),\n        ===(cloudStorageDevice.cloudId),\n        any)(any)\n\n    }\n\n    \"return a valid response when the device doesn't have moments\" in new LoadConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCloudStorageProcess.getCloudStorageDevice(mockApiClient, cloudId) returns serviceRight(\n        cloudStorageDevice.copy(data = cloudStorageDevice.data.copy(moments = None)))\n\n      mockExternalServicesProcess.readFirebaseToken returns serviceRight(tokenFirebase)\n\n      mockCollectionProcess.createCollectionsFromCollectionData(any)(any) returns serviceRight(\n        seqCollection)\n      mockMomentProcess.saveMoments(any)(any) returns serviceRight(seqMoment)\n      mockDeviceProcess.saveDockApps(any) returns serviceRight(seqDockApp)\n      mockUserProcess.updateUserDevice(any, any, any)(any) returns serviceRight(Unit)\n\n      loadConfigurationJobs.loadConfiguration(mockApiClient, cloudId).mustRightUnit\n\n      there was one(mockCollectionProcess).createCollectionsFromCollectionData(\n        ===(toSeqCollectionData(cloudStorageDevice.data.collections)))(any)\n      there was one(mockMomentProcess).saveMoments(===(Seq.empty))(any)\n      there was one(mockDeviceProcess).saveDockApps(===(dockAppSeq getOrElse Seq.empty))\n      there was one(mockUserProcess).updateUserDevice(\n        ===(cloudStorageDevice.data.deviceName),\n        ===(cloudStorageDevice.cloudId),\n        any)(any)\n\n    }\n\n    \"return a valid response when the device doesn't have dockApps\" in new LoadConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCloudStorageProcess.getCloudStorageDevice(mockApiClient, cloudId) returns serviceRight(\n        cloudStorageDevice.copy(data = cloudStorageDevice.data.copy(dockApps = None)))\n\n      mockExternalServicesProcess.readFirebaseToken returns serviceRight(tokenFirebase)\n\n      mockCollectionProcess.createCollectionsFromCollectionData(any)(any) returns serviceRight(\n        seqCollection)\n      mockMomentProcess.saveMoments(any)(any) returns serviceRight(seqMoment)\n      mockDeviceProcess.saveDockApps(any) returns serviceRight(seqDockApp)\n      mockUserProcess.updateUserDevice(any, any, any)(any) returns serviceRight(Unit)\n\n      loadConfigurationJobs.loadConfiguration(mockApiClient, cloudId).mustRightUnit\n\n      there was one(mockCollectionProcess).createCollectionsFromCollectionData(\n        ===(toSeqCollectionData(cloudStorageDevice.data.collections)))(any)\n      there was one(mockMomentProcess).saveMoments(===(momentSeq getOrElse Seq.empty))(any)\n      there was one(mockDeviceProcess).saveDockApps(Seq.empty)\n      there was one(mockUserProcess).updateUserDevice(\n        ===(cloudStorageDevice.data.deviceName),\n        ===(cloudStorageDevice.cloudId),\n        any)(any)\n\n    }\n\n    \"return a JobException when the device doesn't have collections\" in new LoadConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCloudStorageProcess.getCloudStorageDevice(mockApiClient, cloudId) returns serviceRight(\n        cloudStorageDevice.copy(data = cloudStorageDevice.data.copy(collections = Seq.empty)))\n\n      loadConfigurationJobs.loadConfiguration(mockApiClient, cloudId).mustLeft[JobException]\n\n    }\n\n    \"return a CloudStorageProcessException when the service returns an exception\" in new LoadConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCloudStorageProcess.getCloudStorageDevice(mockApiClient, cloudId) returns serviceLeft(\n        cloudStorageProcessException)\n\n      mockExternalServicesProcess.readFirebaseToken returns serviceRight(tokenFirebase)\n\n      loadConfigurationJobs\n        .loadConfiguration(mockApiClient, cloudId)\n        .mustLeft[CloudStorageProcessException]\n\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/wizard/jobs/NewConfigurationJobsSpecification.scala",
    "content": "package cards.nine.app.ui.wizard.jobs\n\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.wizard.jobs.uiactions.{\n  NewConfigurationUiActions,\n  VisibilityUiActions,\n  WizardUiActions\n}\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.{ApiTestData, ApplicationTestData, MomentTestData}\nimport cards.nine.models.types._\nimport cards.nine.process.collection.{CollectionException, CollectionProcess}\nimport cards.nine.process.device.{AppException, DeviceException, DeviceProcess}\nimport cards.nine.process.moment.MomentProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport cards.nine.process.widget.WidgetProcess\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait NewConfigurationJobsSpecification\n    extends TaskServiceSpecification\n    with Mockito\n    with ApiTestData\n    with ApplicationTestData\n    with MomentTestData {\n\n  trait NewConfigurationJobsScope extends Scope {\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    implicit val contextSupport = mock[ContextSupport]\n\n    val deviceException = DeviceException(\"\")\n\n    val collectionException = CollectionException(\"\")\n\n    val appException = AppException(\"\")\n\n    val visibilityUiActions = mock[VisibilityUiActions]\n    visibilityUiActions.hideThirdStep() returns serviceRight(Unit)\n    visibilityUiActions.cleanNewConfiguration() returns serviceRight(Unit)\n    visibilityUiActions.hideFistStepAndShowLoadingBetterCollections(any) returns serviceRight(Unit)\n    visibilityUiActions.hideSecondStepAndShowLoadingSavingCollection returns serviceRight(Unit)\n    visibilityUiActions.showLoadingSavingMoments returns serviceRight(Unit)\n    visibilityUiActions.showNewConfiguration() returns serviceRight(Unit)\n\n    val newConfigurationUiActions = mock[NewConfigurationUiActions]\n    newConfigurationUiActions.loadFifthStep() returns serviceRight(Unit)\n    newConfigurationUiActions.loadSecondStep(any) returns serviceRight(Unit)\n    newConfigurationUiActions.loadThirdStep() returns serviceRight(Unit)\n    newConfigurationUiActions.loadFourthStep(any, any) returns serviceRight(Unit)\n    newConfigurationUiActions.loadFifthStep() returns serviceRight(Unit)\n    newConfigurationUiActions.loadSixthStep() returns serviceRight(Unit)\n\n    val wizardUiActions = mock[WizardUiActions]\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockDeviceProcess = mock[DeviceProcess]\n\n    mockInjector.deviceProcess returns mockDeviceProcess\n\n    val mockCollectionProcess = mock[CollectionProcess]\n\n    mockInjector.collectionProcess returns mockCollectionProcess\n\n    val mockMomentProcess = mock[MomentProcess]\n\n    mockInjector.momentProcess returns mockMomentProcess\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val mockWidgetsProcess = mock[WidgetProcess]\n\n    mockInjector.widgetsProcess returns mockWidgetsProcess\n\n    val newConfigurationJobs =\n      new NewConfigurationJobs(wizardUiActions, newConfigurationUiActions, visibilityUiActions)(\n        contextWrapper) {\n\n        override lazy val di: Injector = mockInjector\n\n      }\n\n  }\n\n}\n\nclass NewConfigurationJobsSpec extends NewConfigurationJobsSpecification {\n\n  \"loadBetterCollections\" should {\n\n    \"return a Seq of PackagesByCategory \" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCollectionProcess.rankApps()(any) returns serviceRight(seqPackagesByCategory)\n\n      newConfigurationJobs.loadBetterCollections().mustRightUnit\n\n      there was one(visibilityUiActions).hideFistStepAndShowLoadingBetterCollections(true)\n      there was one(mockDeviceProcess).resetSavedItems()\n      there was one(mockDeviceProcess).synchronizeInstalledApps(any)\n    }\n\n    \"return a Seq empty if the category of the collections are Misc\" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCollectionProcess.rankApps()(any) returns serviceRight(\n        seqPackagesByCategory map (_.copy(category = Misc)))\n\n      newConfigurationJobs.loadBetterCollections().mustRightUnit\n\n      there was one(visibilityUiActions).hideFistStepAndShowLoadingBetterCollections(true)\n      there was one(mockDeviceProcess).resetSavedItems()\n      there was one(mockDeviceProcess).synchronizeInstalledApps(any)\n    }\n\n    \"return a Seq empty if the packages size of the collections are less than 3\" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCollectionProcess.rankApps()(any) returns serviceRight(\n        seqPackagesByCategory map (_.copy(packages = Seq.empty)))\n\n      newConfigurationJobs.loadBetterCollections().mustRightUnit\n\n      there was one(visibilityUiActions).hideFistStepAndShowLoadingBetterCollections(true)\n      there was one(mockDeviceProcess).resetSavedItems()\n      there was one(mockDeviceProcess).synchronizeInstalledApps(any)\n    }\n\n    \"return a CollectionException when the service returns an exception\" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCollectionProcess.rankApps()(any) returns serviceLeft(collectionException)\n\n      newConfigurationJobs.loadBetterCollections().mustLeft[CollectionException]\n\n      there was one(visibilityUiActions).hideFistStepAndShowLoadingBetterCollections(true)\n      there was one(mockDeviceProcess).resetSavedItems()\n      there was one(mockDeviceProcess).synchronizeInstalledApps(any)\n    }\n  }\n\n  \"rollbackLoadBetterCollections\" should {\n\n    \"return a Seq of PackagesByCategory and clean collections\" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCollectionProcess.rankApps()(any) returns serviceRight(seqPackagesByCategory)\n      mockCollectionProcess.cleanCollections() returns serviceRight(Unit)\n\n      newConfigurationJobs.rollbackLoadBetterCollections().mustRightUnit\n\n      there was one(visibilityUiActions).hideFistStepAndShowLoadingBetterCollections(false)\n      there was one(mockDeviceProcess).resetSavedItems()\n      there was one(mockDeviceProcess).synchronizeInstalledApps(any)\n      there was one(mockCollectionProcess).cleanCollections()\n    }\n\n    \"return a Seq empty if the category of the collections are Misc and clean collections\" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCollectionProcess.rankApps()(any) returns serviceRight(\n        seqPackagesByCategory map (_.copy(category = Misc)))\n      mockCollectionProcess.cleanCollections() returns serviceRight(Unit)\n\n      newConfigurationJobs.rollbackLoadBetterCollections().mustRightUnit\n\n      there was one(visibilityUiActions).hideFistStepAndShowLoadingBetterCollections(false)\n      there was one(mockDeviceProcess).resetSavedItems()\n      there was one(mockDeviceProcess).synchronizeInstalledApps(any)\n    }\n\n    \"return a Seq empty if the packages size of the collections are less than 3 and clean collections\" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCollectionProcess.rankApps()(any) returns serviceRight(\n        seqPackagesByCategory map (_.copy(packages = Seq.empty)))\n      mockCollectionProcess.cleanCollections() returns serviceRight(Unit)\n\n      newConfigurationJobs.rollbackLoadBetterCollections().mustRightUnit\n\n      there was one(visibilityUiActions).hideFistStepAndShowLoadingBetterCollections(false)\n      there was one(mockDeviceProcess).resetSavedItems()\n      there was one(mockDeviceProcess).synchronizeInstalledApps(any)\n    }\n\n    \"return a CollectionException when the service returns an exception\" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.resetSavedItems() returns serviceRight(Unit)\n      mockDeviceProcess.synchronizeInstalledApps(any) returns serviceRight(Unit)\n      mockCollectionProcess.rankApps()(any) returns serviceLeft(collectionException)\n      mockCollectionProcess.cleanCollections() returns serviceRight(Unit)\n\n      newConfigurationJobs.rollbackLoadBetterCollections().mustLeft[CollectionException]\n\n      there was one(visibilityUiActions).hideFistStepAndShowLoadingBetterCollections(false)\n      there was one(mockDeviceProcess).resetSavedItems()\n      there was one(mockDeviceProcess).synchronizeInstalledApps(any)\n    }\n\n  }\n\n  \"saveCollections\" should {\n\n    \"return a DeviceException when the service returns an exception\" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceRight(seqApplicationData)\n\n      newConfigurationJobs.saveCollections(seqPackagesByCategory).mustRightUnit\n\n      there was one(visibilityUiActions).hideSecondStepAndShowLoadingSavingCollection()\n      there was no(mockCollectionProcess).createCollectionsFromCollectionData(any)(any)\n      there was no(mockDeviceProcess).generateDockApps(\n        ===(newConfigurationJobs.defaultDockAppsSize))(any)\n\n    }.pendingUntilFixed(\"Issue #984\")\n\n    \"return a DeviceException when the service returns an exception\" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceLeft(appException)\n\n      newConfigurationJobs.saveCollections(seqPackagesByCategory).mustLeft[AppException]\n\n      there was one(visibilityUiActions).hideSecondStepAndShowLoadingSavingCollection()\n      there was no(mockCollectionProcess).createCollectionsFromCollectionData(any)(any)\n      there was no(mockDeviceProcess).generateDockApps(\n        ===(newConfigurationJobs.defaultDockAppsSize))(any)\n    }\n\n    \"return a DeviceException when the service returns an exception\" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.getSavedApps(any)(any) returns serviceLeft(appException)\n\n      newConfigurationJobs.saveCollections(seqPackagesByCategory).mustLeft[AppException]\n\n      there was one(visibilityUiActions).hideSecondStepAndShowLoadingSavingCollection()\n      there was no(mockCollectionProcess).createCollectionsFromCollectionData(any)(any)\n      there was no(mockDeviceProcess).generateDockApps(\n        ===(newConfigurationJobs.defaultDockAppsSize))(any)\n    }\n  }\n\n  \"loadMomentWithWifi\" should {\n\n    \"return a valid response when getConfiguredNetworks return valid sequence\" in new NewConfigurationJobsScope {\n\n      val networks = 0 to 10 map (c => s\"Networks $c\")\n      mockDeviceProcess.getConfiguredNetworks(any) returns serviceRight(networks)\n\n      newConfigurationJobs.loadMomentWithWifi().mustRightUnit\n\n      there was one(visibilityUiActions).hideThirdStep()\n    }\n\n    \"return a DeviceException when the service returns an exception\" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.getConfiguredNetworks(any) returns serviceLeft(deviceException)\n\n      newConfigurationJobs.loadMomentWithWifi().mustLeft[DeviceException]\n\n      there was one(visibilityUiActions).hideThirdStep()\n    }\n\n  }\n\n  \"rollbackMomentWithWifi\" should {\n\n    \"return a valid response and delete moments and widgets when rollback\" in new NewConfigurationJobsScope {\n\n      val networks = 0 to 10 map (c => s\"Networks $c\")\n      mockDeviceProcess.getConfiguredNetworks(any) returns serviceRight(networks)\n\n      wizardUiActions.showErrorGeneral() returns serviceRight(Unit)\n      visibilityUiActions.cleanNewConfiguration() returns serviceRight(Unit)\n\n      mockMomentProcess.deleteAllMoments() returns serviceRight(Unit)\n      mockWidgetsProcess.deleteAllWidgets() returns serviceRight(Unit)\n\n      newConfigurationJobs.rollbackMomentWithWifi().mustRightUnit\n\n      there was one(wizardUiActions).showErrorGeneral()\n      there was one(visibilityUiActions).cleanNewConfiguration()\n      there was one(mockMomentProcess).deleteAllMoments()\n      there was one(mockWidgetsProcess).deleteAllWidgets()\n    }\n\n    \"return a DeviceException when the service returns an exception\" in new NewConfigurationJobsScope {\n\n      mockDeviceProcess.getConfiguredNetworks(any) returns serviceLeft(deviceException)\n\n      wizardUiActions.showErrorGeneral() returns serviceRight(Unit)\n      visibilityUiActions.cleanNewConfiguration() returns serviceRight(Unit)\n\n      mockMomentProcess.deleteAllMoments() returns serviceRight(Unit)\n      mockWidgetsProcess.deleteAllWidgets() returns serviceRight(Unit)\n\n      newConfigurationJobs.rollbackMomentWithWifi().mustLeft[DeviceException]\n\n    }\n\n  }\n\n  \"saveMomentsWithWifi\" should {\n\n    \"call to saveMoments with Seq.empty and include WalkMoment\" in new NewConfigurationJobsScope {\n\n      val infoMoment = Seq.empty\n      mockTrackEventProcess.chooseMoment(any) returns serviceRight(Unit)\n      mockTrackEventProcess.chooseMomentWifi(any) returns serviceRight(Unit)\n      mockMomentProcess.saveMoments(any)(any) returns serviceRight(Seq.empty)\n      mockCollectionProcess.rankWidgetsByMoment(any, any)(any) returns serviceRight(Seq.empty)\n      mockWidgetsProcess.addWidgets(any) returns serviceRight(Seq.empty)\n      newConfigurationJobs.saveMomentsWithWifi(infoMoment).mustRightUnit\n\n      there was one(visibilityUiActions).showLoadingSavingMoments()\n      there was one(mockTrackEventProcess).chooseMoment(NineCardsMoment.defaultMoment)\n      there was no(mockTrackEventProcess).chooseMomentWifi(any)\n      there was one(mockMomentProcess).saveMoments(===(minMomentsWithWifi))(any)\n    }\n\n    \"call to saveMoments with HomeMorningMoment and include WalkMoment and HomeNightMoment\" in new NewConfigurationJobsScope {\n\n      val infoMoment: Seq[(NineCardsMoment, Option[String])] =\n        Seq((HomeMorningMoment, Option(\"wifi\")))\n      val momentsWithWifi = infoMoment map {\n        case (moment, wifi) => momentData(moment, wifi)\n      }\n      mockTrackEventProcess.chooseMoment(any) returns serviceRight(Unit)\n      mockTrackEventProcess.chooseMomentWifi(any) returns serviceRight(Unit)\n      mockMomentProcess.saveMoments(any)(any) returns serviceRight(Seq.empty)\n      mockCollectionProcess.rankWidgetsByMoment(any, any)(any) returns serviceRight(Seq.empty)\n      mockWidgetsProcess.addWidgets(any) returns serviceRight(Seq.empty)\n      newConfigurationJobs.saveMomentsWithWifi(infoMoment).mustRightUnit\n\n      there was one(visibilityUiActions).showLoadingSavingMoments()\n      there was three(mockTrackEventProcess).chooseMoment(any)\n      there was two(mockTrackEventProcess).chooseMomentWifi(any)\n      there was one(mockMomentProcess).saveMoments(\n        ===(momentsWithWifi ++ minMomentsWithWifi ++ homeNightMoment))(any)\n    }\n\n    \"call to saveMoments with other moments and include WalkMoment\" in new NewConfigurationJobsScope {\n\n      val infoMoment: Seq[(NineCardsMoment, Option[String])] =\n        Seq((WorkMoment, Option(\"wifi\")), (StudyMoment, Option(\"wifi1\")))\n\n      val momentsWithWifi = infoMoment map {\n        case (moment, wifi) => momentData(moment, wifi)\n      }\n      mockTrackEventProcess.chooseMoment(any) returns serviceRight(Unit)\n      mockTrackEventProcess.chooseMomentWifi(any) returns serviceRight(Unit)\n      mockMomentProcess.saveMoments(any)(any) returns serviceRight(Seq.empty)\n      mockCollectionProcess.rankWidgetsByMoment(any, any)(any) returns serviceRight(Seq.empty)\n      mockWidgetsProcess.addWidgets(any) returns serviceRight(Seq.empty)\n      newConfigurationJobs.saveMomentsWithWifi(infoMoment).mustRightUnit\n\n      there was one(visibilityUiActions).showLoadingSavingMoments()\n      there was three(mockTrackEventProcess).chooseMoment(any)\n      there was two(mockTrackEventProcess).chooseMomentWifi(any)\n      there was one(mockMomentProcess).saveMoments(===(momentsWithWifi ++ minMomentsWithWifi))(any)\n    }\n\n  }\n\n  \"saveMoments\" should {\n\n    \"call to saveMoments with Seq.empty\" in new NewConfigurationJobsScope {\n\n      val moments: Seq[NineCardsMoment] = Seq.empty\n\n      mockTrackEventProcess.chooseMoment(any) returns serviceRight(Unit)\n      mockTrackEventProcess.chooseMomentWifi(any) returns serviceRight(Unit)\n      mockMomentProcess.saveMoments(any)(any) returns serviceRight(Seq.empty)\n      mockCollectionProcess.rankWidgetsByMoment(any, any)(any) returns serviceRight(Seq.empty)\n      mockWidgetsProcess.addWidgets(any) returns serviceRight(Seq.empty)\n      newConfigurationJobs.saveMoments(moments).mustRightUnit\n\n      there was one(visibilityUiActions).showLoadingSavingMoments()\n      there was no(mockTrackEventProcess).chooseMoment(any)\n      there was no(mockTrackEventProcess).chooseMomentWifi(any)\n      there was one(mockMomentProcess).saveMoments(===(Seq.empty))(any)\n    }\n\n    \"call to saveMoments with the right param \" in new NewConfigurationJobsScope {\n\n      val moments: Seq[NineCardsMoment] =\n        Seq(HomeMorningMoment, MusicMoment, CarMoment, SportMoment, OutAndAboutMoment)\n      val momentsWithoutWifi = moments map { moment =>\n        momentData(moment, None)\n      }\n      mockTrackEventProcess.chooseMoment(any) returns serviceRight(Unit)\n      mockTrackEventProcess.chooseMomentWifi(any) returns serviceRight(Unit)\n      mockMomentProcess.saveMoments(any)(any) returns serviceRight(Seq.empty)\n      mockCollectionProcess.rankWidgetsByMoment(any, any)(any) returns serviceRight(Seq.empty)\n      mockWidgetsProcess.addWidgets(any) returns serviceRight(Seq.empty)\n      newConfigurationJobs.saveMoments(moments).mustRightUnit\n\n      there was one(visibilityUiActions).showLoadingSavingMoments()\n      there was atLeastThree(mockTrackEventProcess).chooseMoment(any)\n      there was no(mockTrackEventProcess).chooseMomentWifi(any)\n      there was one(mockMomentProcess).saveMoments(===(momentsWithoutWifi))(any)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/app/ui/wizard/jobs/WizardJobsSpecification.scala",
    "content": "package cards.nine.app.ui.wizard.jobs\n\nimport android.content.res.Resources\nimport cards.nine.app.di.Injector\nimport cards.nine.app.ui.commons.{JobException, RequestCodes}\nimport cards.nine.app.ui.wizard.WizardMarketTokenRequestCancelledException\nimport cards.nine.app.ui.wizard.jobs.uiactions.{VisibilityUiActions, WizardUiActions}\nimport cards.nine.commons.contexts.{ActivityContextSupport, ContextSupport}\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.CloudStorageTestData\nimport cards.nine.commons.test.data.WizardJobsValues._\nimport cards.nine.models.types.{CallPhone, FineLocation, PermissionResult}\nimport cards.nine.process.accounts.{UserAccountsProcess, UserAccountsProcessPermissionException}\nimport cards.nine.process.cloud.CloudStorageProcess\nimport cards.nine.process.social.SocialProfileProcess\nimport cards.nine.process.trackevent.TrackEventProcess\nimport cards.nine.process.user.UserProcess\nimport com.google.android.gms.common.api.GoogleApiClient\nimport macroid.ActivityContextWrapper\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait WizardJobsSpecification extends TaskServiceSpecification with Mockito {\n\n  trait WizardJobsScope extends Scope with CloudStorageTestData {\n\n    val exception = new Throwable(\"\")\n\n    implicit val contextWrapper = mock[ActivityContextWrapper]\n\n    implicit val contextSupport = mock[ContextSupport]\n\n    contextSupport.getResources returns mockResources\n\n    val mockResources = mock[Resources]\n\n    val mockActivityContextSupport = mock[ActivityContextSupport]\n\n    val userAccountProcessException = UserAccountsProcessPermissionException(\"\")\n\n    val wizardMarketTokenRequestCancelledException = WizardMarketTokenRequestCancelledException(\"\")\n\n    val mockInjector: Injector = mock[Injector]\n\n    val mockWizardUiAction = mock[WizardUiActions]\n\n    val mockVisibilityUiActions = mock[VisibilityUiActions]\n\n    val mockUserAccountsProcess = mock[UserAccountsProcess]\n\n    mockInjector.userAccountsProcess returns mockUserAccountsProcess\n\n    val mockCloudStorageProcess = mock[CloudStorageProcess]\n\n    mockInjector.cloudStorageProcess returns mockCloudStorageProcess\n\n    val mockApiClient = mock[GoogleApiClient]\n\n    val mockSocialProfileProcess = mock[SocialProfileProcess]\n\n    mockInjector.socialProfileProcess returns mockSocialProfileProcess\n\n    val mockUserProcess = mock[UserProcess]\n\n    mockInjector.userProcess returns mockUserProcess\n\n    val mockTrackEventProcess = mock[TrackEventProcess]\n\n    mockInjector.trackEventProcess returns mockTrackEventProcess\n\n    val wizardJobs = new WizardJobs(mockWizardUiAction, mockVisibilityUiActions)(contextWrapper) {\n      override lazy val di: Injector = mockInjector\n\n      override def getString(res: Int): String = \"\"\n    }\n  }\n\n}\n\nclass WizardJobsSpec extends WizardJobsSpecification {\n\n  \"initialize\" should {\n    \"call to initialize\" in new WizardJobsScope {\n\n      mockWizardUiAction.initialize() returns serviceRight(Unit)\n      mockVisibilityUiActions.goToUser() returns serviceRight(Unit)\n\n      wizardJobs.initialize().mustRightUnit\n\n      there was one(mockWizardUiAction).initialize()\n      there was one(mockVisibilityUiActions).goToUser()\n\n    }\n  }\n\n  \"stop\" should {\n    \"call to stop\" in new WizardJobsScope {\n\n      mockApiClient.disconnect()\n      wizardJobs.stop().mustRightUnit\n\n      there was one(mockApiClient).disconnect()\n\n    }\n  }\n\n  \"deviceSelected\" should {\n    \"return a valid response when the service returns a right response\" in new WizardJobsScope {\n\n      mockUserAccountsProcess.havePermission(any)(any) returns serviceRight(\n        PermissionResult(FineLocation, result = true))\n      mockVisibilityUiActions.goToWizard(any) returns serviceRight(Unit)\n      mockTrackEventProcess.chooseExistingDevice() returns serviceRight(Unit)\n\n      wizardJobs.deviceSelected(Option(keyDevice)).mustRightUnit\n\n      there was one(mockUserAccountsProcess).havePermission(===(FineLocation))(any)\n      there was one(mockVisibilityUiActions).goToWizard(keyDevice)\n      there was one(mockTrackEventProcess).chooseExistingDevice()\n    }\n\n    \"return a valid response when the service returns that haven't permissions\" in new WizardJobsScope {\n\n      mockUserAccountsProcess.havePermission(any)(any) returns serviceRight(\n        PermissionResult(FineLocation, result = false))\n      mockUserAccountsProcess.requestPermission(any, any)(any) returns serviceRight(Unit)\n      mockTrackEventProcess.chooseExistingDevice() returns serviceRight(Unit)\n\n      wizardJobs.deviceSelected(Option(keyDevice)).mustRightUnit\n\n      there was one(mockUserAccountsProcess).havePermission(===(FineLocation))(any)\n      there was no(mockVisibilityUiActions).goToWizard(keyDevice)\n      there was one(mockUserAccountsProcess)\n        .requestPermission(===(RequestCodes.wizardPermissions), ===(FineLocation))(any)\n      there was one(mockTrackEventProcess).chooseExistingDevice()\n    }\n\n    \"return a valid response if deviceKey is None\" in new WizardJobsScope {\n\n      mockUserAccountsProcess.havePermission(any)(any) returns serviceRight(\n        PermissionResult(FineLocation, result = true))\n      mockVisibilityUiActions.goToNewConfiguration(any) returns serviceRight(Unit)\n      mockTrackEventProcess.chooseNewConfiguration() returns serviceRight(Unit)\n\n      wizardJobs.deviceSelected(None).mustRightUnit\n\n      there was one(mockUserAccountsProcess).havePermission(===(FineLocation))(any)\n      there was one(mockVisibilityUiActions).goToNewConfiguration(any)\n      there was no(mockVisibilityUiActions).goToWizard(any)\n      there was one(mockTrackEventProcess).chooseNewConfiguration()\n    }\n\n  }\n\n  \"requestPermissions\" should {\n\n    \"call with wizardPermissions\" in new WizardJobsScope {\n\n      mockUserAccountsProcess.requestPermission(any, any)(any) returns serviceRight(Unit)\n      wizardJobs.requestPermissions().mustRightUnit\n\n      there was one(mockUserAccountsProcess)\n        .requestPermission(===(RequestCodes.wizardPermissions), ===(FineLocation))(any)\n    }\n\n    \"return a UserAccountsProcessPermissionException when the service returns an exception\" in new WizardJobsScope {\n\n      mockUserAccountsProcess.requestPermission(any, any)(any) returns serviceLeft(\n        userAccountProcessException)\n      wizardJobs.requestPermissions().mustLeft[UserAccountsProcessPermissionException]\n\n      there was one(mockUserAccountsProcess)\n        .requestPermission(===(RequestCodes.wizardPermissions), ===(FineLocation))(any)\n    }\n  }\n\n  \"permissionDialogCancelled\" should {\n\n    \"return a valid response if deviceKey is right\" in new WizardJobsScope {\n\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(deviceKey = Option(keyDevice))\n      mockVisibilityUiActions.goToWizard(any) returns serviceRight(Unit)\n\n      wizardJobs.permissionDialogCancelled().mustRightUnit\n      there was one(mockVisibilityUiActions).goToWizard(keyDevice)\n    }\n\n    \"return a valid response if deviceKey is None\" in new WizardJobsScope {\n\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(deviceKey = None)\n      mockVisibilityUiActions.goToNewConfiguration(any) returns serviceRight(Unit)\n\n      wizardJobs.permissionDialogCancelled().mustRightUnit\n      there was one(mockVisibilityUiActions).goToNewConfiguration(any)\n      there was no(mockVisibilityUiActions).goToWizard(any)\n    }\n  }\n\n  \"requestAndroidMarketPermission\" should {\n\n    \"return an Answer and call goToUser if the client hasn't email\" in new WizardJobsScope {\n\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(email = None)\n      mockVisibilityUiActions.goToUser() returns serviceRight(Unit)\n\n      wizardJobs.requestAndroidMarketPermission().mustRightUnit\n\n      there was one(mockVisibilityUiActions).goToUser()\n\n    }\n\n    \"return an Answer if the client hasn't androidMarketToken\" in new WizardJobsScope {\n\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(email = Option(email))\n      mockCloudStorageProcess.createCloudStorageClient(any)(any) returns serviceRight(\n        mockApiClient)\n\n      mockVisibilityUiActions.showLoadingConnectingWithGoogle() returns serviceRight(Unit)\n      mockUserAccountsProcess.getAuthToken(any, any)(any) returns serviceRight(token)\n      mockVisibilityUiActions.showLoadingRequestGooglePermission() returns serviceRight(Unit)\n\n      wizardJobs.requestAndroidMarketPermission().mustRightUnit\n\n      there was no(mockVisibilityUiActions).goToUser()\n      there was one(mockCloudStorageProcess).createCloudStorageClient(===(email))(any)\n      there was one(mockVisibilityUiActions).showLoadingConnectingWithGoogle()\n      there was two(mockUserAccountsProcess).getAuthToken(===(email), any)(any)\n\n    }\n\n    \"return a WizardMarketTokenRequestCancelledException if the client hasn't androidMarketToken and the service returns an exception\" in new WizardJobsScope {\n\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(email = Option(email))\n      mockCloudStorageProcess.createCloudStorageClient(any)(any) returns serviceRight(\n        mockApiClient)\n      mockVisibilityUiActions.showLoadingConnectingWithGoogle() returns serviceRight(Unit)\n      mockUserAccountsProcess.getAuthToken(anyString, anyString)(any) returns serviceLeft(\n        WizardMarketTokenRequestCancelledException(\"\"))\n      mockVisibilityUiActions.showLoadingRequestGooglePermission() returns serviceRight(Unit)\n\n      wizardJobs\n        .requestAndroidMarketPermission()\n        .mustLeft[WizardMarketTokenRequestCancelledException]\n\n      there was no(mockVisibilityUiActions).goToUser()\n      there was one(mockCloudStorageProcess).createCloudStorageClient(===(email))(any)\n      there was one(mockVisibilityUiActions).showLoadingConnectingWithGoogle()\n      there was one(mockUserAccountsProcess).getAuthToken(===(email), any)(any)\n\n    }\n  }\n\n  \"requestGooglePermission\" should {\n\n    \"return an Answer and call goToUser if the client hasn't email\" in new WizardJobsScope {\n\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(email = None)\n      mockVisibilityUiActions.goToUser() returns serviceRight(Unit)\n\n      wizardJobs.requestGooglePermission().mustRightUnit\n\n      there was one(mockVisibilityUiActions).goToUser()\n    }\n\n    \"return an Answer when the client has email\" in new WizardJobsScope {\n\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(email = Option(email))\n      wizardJobs.clientStatuses =\n        wizardJobs.clientStatuses.copy(driveApiClient = Option(mockApiClient))\n      mockVisibilityUiActions.showLoadingRequestGooglePermission() returns serviceRight(Unit)\n      mockUserAccountsProcess.getAuthToken(any, any)(any) returns serviceRight(token)\n\n      wizardJobs.requestGooglePermission().mustRightUnit\n\n      there was no(mockVisibilityUiActions).goToUser()\n    }\n\n    \"return an Answer when the client has email and hasn't driveApiClient\" in new WizardJobsScope {\n\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(email = Option(email))\n      mockVisibilityUiActions.showLoadingRequestGooglePermission() returns serviceRight(Unit)\n      mockUserAccountsProcess.getAuthToken(any, any)(any) returns serviceRight(token)\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(driveApiClient = None)\n\n      mockCloudStorageProcess.createCloudStorageClient(any)(any) returns serviceRight(\n        mockApiClient)\n      mockVisibilityUiActions.showLoadingConnectingWithGoogle() returns serviceRight(Unit)\n\n      wizardJobs.requestGooglePermission().mustRightUnit\n\n      there was no(mockVisibilityUiActions).goToUser()\n      there was one(mockCloudStorageProcess).createCloudStorageClient(===(email))(any)\n      there was one(mockVisibilityUiActions).showLoadingConnectingWithGoogle()\n      there was two(mockVisibilityUiActions).showLoadingRequestGooglePermission()\n      there was three(mockUserAccountsProcess).getAuthToken(===(email), any)(any)\n    }\n  }\n\n  \"requestPermissionsResult\" should {\n\n    \"return a Unit when hasn't wizard permissions\" in new WizardJobsScope {\n\n      wizardJobs.requestPermissionsResult(requestCodeError, permissions, granResults).mustRightUnit\n\n    }\n\n    \"return a Unit when resquestCode is WizardPermmisons and has FineLocation but result is false and hasn't deviceKey\" in new WizardJobsScope {\n\n      val permissionResultFalse = PermissionResult(FineLocation, result = false)\n      mockUserAccountsProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(permissionResultFalse))\n      mockUserAccountsProcess.shouldRequestPermission(any)(any) returns serviceRight(\n        permissionResultFalse)\n      mockVisibilityUiActions.goToNewConfiguration(any) returns serviceRight(Unit)\n\n      wizardJobs.requestPermissionsResult(requestCode, permissions, granResults).mustRightUnit\n\n      there was one(mockUserAccountsProcess)\n        .parsePermissionsRequestResult(permissions, granResults)\n      there was one(mockUserAccountsProcess).shouldRequestPermission(===(FineLocation))(any)\n      there was one(mockVisibilityUiActions).goToNewConfiguration(any)\n      there was no(mockVisibilityUiActions).goToWizard(any)\n\n    }\n\n    \"return a Unit when resquestCode is WizardPermmisons and has FineLocation but result is false and has deviceKey\" in new WizardJobsScope {\n\n      val permissionResultFalse = PermissionResult(FineLocation, result = false)\n      mockUserAccountsProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(permissionResultFalse))\n      mockUserAccountsProcess.shouldRequestPermission(any)(any) returns serviceRight(\n        permissionResultFalse)\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(deviceKey = Option(keyDevice))\n      mockVisibilityUiActions.goToWizard(any) returns serviceRight(Unit)\n\n      wizardJobs.requestPermissionsResult(requestCode, permissions, granResults).mustRightUnit\n\n      there was one(mockUserAccountsProcess)\n        .parsePermissionsRequestResult(permissions, granResults)\n      there was one(mockUserAccountsProcess).shouldRequestPermission(===(FineLocation))(any)\n      there was one(mockVisibilityUiActions).goToWizard(keyDevice)\n\n    }\n\n    \"return a Unit when resquestCode is WizardPermmisons and has FineLocation but result is true and hasn't deviceKey\" in new WizardJobsScope {\n\n      val permissionResult = PermissionResult(FineLocation, result = true)\n      mockUserAccountsProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(permissionResult))\n      mockUserAccountsProcess.shouldRequestPermission(any)(any) returns serviceRight(\n        permissionResult)\n      mockVisibilityUiActions.goToNewConfiguration(any) returns serviceRight(Unit)\n\n      wizardJobs.requestPermissionsResult(requestCode, permissions, granResults).mustRightUnit\n\n      there was one(mockUserAccountsProcess)\n        .parsePermissionsRequestResult(permissions, granResults)\n      there was one(mockUserAccountsProcess).shouldRequestPermission(===(FineLocation))(any)\n      there was one(mockVisibilityUiActions).goToNewConfiguration(any)\n      there was no(mockVisibilityUiActions).goToWizard(any)\n\n    }\n\n    \"return a Unit when resquestCode is WizardPermmisons and has FineLocation but result is true and has deviceKey\" in new WizardJobsScope {\n\n      val permissionResult = PermissionResult(FineLocation, result = true)\n      mockUserAccountsProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(permissionResult))\n      mockUserAccountsProcess.shouldRequestPermission(any)(any) returns serviceRight(\n        permissionResult)\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(deviceKey = Option(keyDevice))\n      mockVisibilityUiActions.goToWizard(any) returns serviceRight(Unit)\n\n      wizardJobs.requestPermissionsResult(requestCode, permissions, granResults).mustRightUnit\n\n      there was one(mockUserAccountsProcess)\n        .parsePermissionsRequestResult(permissions, granResults)\n      there was one(mockUserAccountsProcess).shouldRequestPermission(===(FineLocation))(any)\n      there was one(mockVisibilityUiActions).goToWizard(keyDevice)\n\n    }\n\n    \"return a Unit when resquestCode is WizardPermmisons and has CallPhone but result is true\" in new WizardJobsScope {\n\n      val permissionResult = PermissionResult(CallPhone, result = true)\n      mockUserAccountsProcess.parsePermissionsRequestResult(any, any) returns serviceRight(\n        Seq(permissionResult))\n      mockUserAccountsProcess.shouldRequestPermission(any)(any) returns serviceRight(\n        permissionResult)\n      mockWizardUiAction.showRequestPermissionsDialog() returns serviceRight(Unit)\n\n      wizardJobs.requestPermissionsResult(requestCode, permissions, granResults).mustRightUnit\n\n      there was one(mockUserAccountsProcess)\n        .parsePermissionsRequestResult(permissions, granResults)\n      there was one(mockUserAccountsProcess).shouldRequestPermission(===(FineLocation))(any)\n      there was one(mockWizardUiAction).showRequestPermissionsDialog()\n\n    }\n  }\n\n  \"errorOperationMarketTokenCancelled\" should {\n    \"return a valid response when the service returns a right response\" in new WizardJobsScope {\n\n      mockWizardUiAction.showMarketPermissionDialog() returns serviceRight(Unit)\n\n      wizardJobs.errorOperationMarketTokenCancelled().mustRightUnit\n\n      there was one(mockWizardUiAction).showMarketPermissionDialog()\n    }\n  }\n\n  \"errorOperationGoogleTokenCancelled\" should {\n    \"return a valid response when the service returns a right response\" in new WizardJobsScope {\n\n      mockWizardUiAction.showGooglePermissionDialog() returns serviceRight(Unit)\n\n      wizardJobs.errorOperationGoogleTokenCancelled().mustRightUnit\n\n      there was one(mockWizardUiAction).showGooglePermissionDialog()\n    }\n  }\n\n  \"plusConnected\" should {\n    \"return an Answer when the client hasn't plusApiClient\" in new WizardJobsScope {\n\n      mockVisibilityUiActions.goToUser() returns serviceRight(Unit)\n\n      wizardJobs.plusConnected().mustRightUnit\n\n      there was one(mockVisibilityUiActions).goToUser()\n\n    }\n\n    \"return an Answer when the client has plusApiClient\" in new WizardJobsScope {\n\n      wizardJobs.clientStatuses =\n        wizardJobs.clientStatuses.copy(plusApiClient = Option(mockApiClient))\n      mockVisibilityUiActions.showLoadingConnectingWithGooglePlus() returns serviceRight(Unit)\n      mockSocialProfileProcess.updateUserProfile(any)(any) returns serviceRight(\n        Option(profileName))\n      mockWizardUiAction.showErrorConnectingGoogle() returns serviceRight(Unit)\n      mockVisibilityUiActions.goToUser() returns serviceRight(Unit)\n\n      wizardJobs.plusConnected().mustRightUnit\n\n      there was one(mockVisibilityUiActions).goToUser()\n      there was one(mockVisibilityUiActions).showLoadingConnectingWithGooglePlus()\n      there was one(mockSocialProfileProcess).updateUserProfile(===(mockApiClient))(any)\n\n    }\n\n    \"return an Answer when the WizardStatus has all\" in new WizardJobsScope {\n\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(\n        plusApiClient = Option(mockApiClient),\n        driveApiClient = Option(mockApiClient),\n        email = Option(email),\n        androidMarketToken = Option(token),\n        mailTokenId = Option(emailTokenId))\n      mockVisibilityUiActions.showLoadingConnectingWithGooglePlus() returns serviceRight(Unit)\n      mockSocialProfileProcess.updateUserProfile(any)(any) returns serviceRight(\n        Option(profileName))\n      mockVisibilityUiActions.showLoadingDevices() returns serviceRight(Unit)\n\n      mockUserProcess.signIn(any, any, any)(any) returns serviceRight(Unit)\n      mockCloudStorageProcess.getCloudStorageDevices(any)(any) returns serviceRight(\n        Seq(generateCloudStorageDeviceSummary()))\n\n      mockWizardUiAction.showDevices(any) returns serviceRight(Unit)\n\n      wizardJobs.plusConnected().mustRightUnit\n\n      there was one(mockVisibilityUiActions).goToUser()\n      there was one(mockVisibilityUiActions).showLoadingConnectingWithGooglePlus()\n      there was one(mockSocialProfileProcess).updateUserProfile(===(mockApiClient))(any)\n      there was one(mockVisibilityUiActions).showLoadingDevices()\n      there was one(mockUserProcess).signIn(===(email), ===(token), ===(emailTokenId))(any)\n      there was one(mockCloudStorageProcess).getCloudStorageDevices(===(mockApiClient))(any)\n\n    }.pendingUntilFixed\n  }\n\n  \"googleSignIn\" should {\n\n    \"return an Answer when hasn't email\" in new WizardJobsScope {\n\n      mockVisibilityUiActions.goToUser() returns serviceRight(Unit)\n\n      wizardJobs.googleSignIn().mustRightUnit\n\n      there was one(mockVisibilityUiActions).goToUser()\n\n    }\n  }\n\n  \"googleDriveClient\" should {\n    \"return a JobException when hasn't driveApiClient\" in new WizardJobsScope {\n\n      wizardJobs.clientStatuses = wizardJobs.clientStatuses.copy(driveApiClient = None)\n      wizardJobs.googleDriveClient.mustLeft[JobException]\n    }\n\n    \"return a JobException when has driveApiClient and the client hasn't connected\" in new WizardJobsScope {\n\n      mockApiClient.isConnected returns false\n      wizardJobs.clientStatuses =\n        wizardJobs.clientStatuses.copy(driveApiClient = Option(mockApiClient))\n      wizardJobs.googleDriveClient.mustLeft[JobException]\n\n    }\n\n    \"return an Answer when has driveApiClient and the client has connected\" in new WizardJobsScope {\n\n      mockApiClient.isConnected returns true\n      wizardJobs.clientStatuses =\n        wizardJobs.clientStatuses.copy(driveApiClient = Option(mockApiClient))\n      wizardJobs.googleDriveClient.run must beLike {\n        case Right(result) => result shouldEqual mockApiClient\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/process/cloud/impl/CloudStorageProcessImplData.scala",
    "content": "package cards.nine.process.cloud.impl\n\nimport cards.nine.commons.test.data.UserTestData\nimport cards.nine.models.types.{Business, FreeCollectionType}\nimport cards.nine.services.drive.models.{DriveServiceFile, DriveServiceFileSummary}\nimport org.joda.time.DateTime\nimport cards.nine.commons.test.data.CloudStorageValues._\n\nimport scala.util.Random\n\ntrait CloudStorageProcessImplData extends UserTestData {\n\n  val driveServiceFileSummary = generateDriveServiceFileSummary\n  val driveServiceFileSummarySeq: Seq[DriveServiceFileSummary] = 1 to 10 map (_ =>\n                                                                                generateDriveServiceFileSummary)\n  val driveServiceFileSummaryEmptySeq = Seq.empty[DriveServiceFileSummary]\n\n  def generateDriveServiceFileSummary =\n    DriveServiceFileSummary(\n      uuid = java.util.UUID.randomUUID.toString,\n      deviceId = Option(Random.nextString(10)),\n      title = Random.nextString(10),\n      createdDate = DateTime.now().minusMonths(6).toDate,\n      modifiedDate = DateTime.now().minusMonths(3).toDate)\n\n  val validCloudStorageDeviceJson =\n    s\"\"\"\n       |{\n       | \"deviceId\": \"$deviceId\",\n       | \"deviceName\": \"$deviceName\",\n       | \"documentVersion\": $documentVersion,\n       | \"collections\": [${generateCollectionsJson(numCollections, numItemsPerCollection).mkString(\n         \",\")}],\n       | \"moments\": [${generateMomentsJson(numMoments, numTimeSlot).mkString(\",\")}],\n       | \"dockApps\": [${generateDockAppsJson(numDockApps).mkString(\",\")}]\n       |}\n    \"\"\".stripMargin\n\n  def generateCollectionsJson(num: Int, numItems: Int): Seq[String] = 1 to num map { i =>\n    s\"\"\"\n       |{\n       | \"name\": \"Collection $num\",\n       | \"originalSharedCollectionId\": \"Original Shared Collection Id $num\",\n       | \"sharedCollectionId\": \"Shared Collection Id $num\",\n       | \"sharedCollectionSubscribed\": true,\n       | \"items\": [${generateCollectionItemsJson(numItems).mkString(\",\")}],\n       | \"collectionType\": \"${FreeCollectionType.name}\",\n       | \"icon\": \"Collection Icon $num\",\n       | \"category\": \"${Business.name}\"\n       |}\n    \"\"\".stripMargin\n  }\n\n  def generateCollectionItemsJson(num: Int): Seq[String] = 1 to num map { i =>\n    s\"\"\"\n       |{\n       | \"itemType\": \"Item Type $num\",\n       | \"title\": \"Item Title $num\",\n       | \"intent\": \"Item intent $num\"\n       |}\n    \"\"\".stripMargin\n  }\n\n  def generateMomentsJson(num: Int, numItems: Int): Seq[String] = 1 to num map { i =>\n    s\"\"\"\n       |{\n       | \"timeslot\": [${generateTimeSlotJson(numItems).mkString(\",\")}],\n       | \"wifi\": [\"Wifi_Network $num\", \"Mobile $num \"],\n       | \"headphones\": false,\n       | \"momentType\": \"HOME\",\n       | \"widgets\": [${generateWidgetJson(numWidgets).mkString(\",\")}]\n       |}\n    \"\"\".stripMargin\n  }\n\n  def generateTimeSlotJson(num: Int): Seq[String] = 1 to num map { i =>\n    s\"\"\"\n       |{\n       | \"from\": \"8:00\",\n       | \"to\": \"19:00\",\n       | \"days\": [0, 1, 1, 1, 1, 1, 0]\n       |}\n    \"\"\".stripMargin\n  }\n\n  def generateWidgetJson(num: Int): Seq[String] = 1 to num map { i =>\n    s\"\"\"\n       |{\n       | \"packageName\": \"package-name\",\n       | \"className\": \"class-name\",\n       | \"area\": ${generateWidgetAreaJson(i)},\n       | \"widgetType\": \"APP\"\n       |}\n    \"\"\".stripMargin\n  }\n\n  def generateWidgetAreaJson(num: Int): String =\n    s\"\"\"\n       |{\n       | \"startX\": $num,\n       | \"startY\": $num,\n       | \"spanX\": 1,\n       | \"spanY\": 1\n       |}\n  \"\"\".stripMargin\n\n  def generateDockAppsJson(num: Int): Seq[String] = 1 to num map { i =>\n    s\"\"\"\n       |{\n       | \"name\": \"DockApp $num\",\n       | \"dockType\": \"APP\",\n       | \"intent\": \"Item intent $num\",\n       | \"imagePath\": \"/path/to/image/$num\",\n       | \"position\": $num\n       |}\n    \"\"\".stripMargin\n  }\n\n  val invalidCloudStorageDeviceJson =\n    \"\"\"\n      |{ \"inexistendField\": \"Value\" }\n    \"\"\".stripMargin\n\n  val driveServiceFile = DriveServiceFile(driveServiceFileSummary, validCloudStorageDeviceJson)\n\n  val invalidDriveServiceFileJson =\n    DriveServiceFile(driveServiceFileSummary, invalidCloudStorageDeviceJson)\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/process/cloud/impl/CloudStorageProcessImplSpecification.scala",
    "content": "package cards.nine.process.cloud.impl\n\nimport android.content.Context\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.data.CloudStorageTestData\nimport cards.nine.process.cloud.{CloudStorageClientListener, CloudStorageProcessException}\nimport cards.nine.services.drive.{DriveServices, DriveServicesException}\nimport cards.nine.services.persistence.{\n  AndroidIdNotFoundException,\n  PersistenceServiceException,\n  PersistenceServices\n}\nimport monix.eval.Task\nimport org.hamcrest.{Description, TypeSafeMatcher}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\nimport play.api.libs.json.Json\nimport cats.syntax.either._\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.models.RawCloudStorageDevice\nimport com.google.android.gms.common.api.GoogleApiClient\nimport cards.nine.commons.test.data.CloudStorageValues._\n\nimport scala.ref.WeakReference\n\ntrait CloudStorageProcessImplSpecification extends TaskServiceSpecification with Mockito {\n\n  val driveServicesException = DriveServicesException(\"\")\n\n  val persistenceServicesException = PersistenceServiceException(\"\")\n\n  val androidIdNotFoundException = AndroidIdNotFoundException(\"\")\n\n  val sampleId = \"android-id\"\n\n  trait CloudStorageProcessImplScope\n      extends Scope\n      with CloudStorageProcessImplData\n      with CloudStorageTestData {\n\n    implicit val mockContextSupport = mock[ContextSupport]\n\n    val mockContext = mock[Context]\n\n    val mockContextListener = mock[MyListener]\n\n    val driveServices = mock[DriveServices]\n\n    val persistenceServices = mock[PersistenceServices]\n\n    val mockApiClient = mock[GoogleApiClient]\n\n    persistenceServices.getAndroidId returns TaskService(Task(Either.right(sampleId)))\n\n    val cloudStorageProcess = new CloudStorageProcessImpl(driveServices, persistenceServices)\n\n  }\n\n  abstract class MyListener extends Context with CloudStorageClientListener\n\n  class JsonMatcher(json: String) extends TypeSafeMatcher[String] {\n\n    val expected = Json.parse(json)\n\n    override def matchesSafely(item: String): Boolean =\n      expected == Json.parse(item)\n\n    override def describeTo(description: Description): Unit =\n      description.appendText(\"Json are not equivalent\")\n  }\n\n}\n\nclass CloudStorageProcessImplSpec extends CloudStorageProcessImplSpecification {\n\n  \"createCloudStorageClient\" should {\n\n    \"return a valid response when the service returns a right response\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getOriginal returns new WeakReference(mockContextListener)\n        driveServices.createDriveClient(any)(any) returns TaskService(Task(Right(mockApiClient)))\n        cloudStorageProcess.createCloudStorageClient(account).run shouldEqual Right(mockApiClient)\n      }\n\n    \"return a CloudStorageProcessException when the context doesn't implement CloudStorageClientListener\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getOriginal returns new WeakReference(mockContext)\n        driveServices.createDriveClient(any)(any) returns TaskService(\n          Task(Either.left(driveServicesException)))\n        cloudStorageProcess\n          .createCloudStorageClient(account)\n          .mustLeft[CloudStorageProcessException]\n      }\n\n    \"return a CloudStorageProcessException when the context doesn't exists\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getOriginal returns new WeakReference(null)\n        driveServices.createDriveClient(any)(any) returns TaskService(\n          Task(Either.left(driveServicesException)))\n        cloudStorageProcess\n          .createCloudStorageClient(account)\n          .mustLeft[CloudStorageProcessException]\n      }\n\n    \"return a CloudStorageProcessException when the service returns an exception\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getOriginal returns new WeakReference(mockContextListener)\n        driveServices.createDriveClient(any)(any) returns TaskService(\n          Task(Either.left(driveServicesException)))\n        cloudStorageProcess\n          .createCloudStorageClient(account)\n          .mustLeft[CloudStorageProcessException]\n      }\n\n  }\n\n  \"prepareForActualDevice\" should {\n\n    \"return the actual device and an empty list when passing only one element that corresponds to the device id\" in\n      new CloudStorageProcessImplScope {\n\n        persistenceServices.getAndroidId returns TaskService(Task(Either.right(deviceId)))\n\n        val result =\n          cloudStorageProcess.prepareForActualDevice(mockApiClient, Seq(cloudStorageDevice)).run\n        result shouldEqual Right(Some(cloudStorageDevice), Seq.empty)\n\n      }\n\n    \"return an empty option and an empty list when passing a empty list\" in\n      new CloudStorageProcessImplScope {\n\n        persistenceServices.getAndroidId returns TaskService(Task(Either.right(deviceId)))\n\n        val result = cloudStorageProcess.prepareForActualDevice(mockApiClient, Seq.empty).run\n        result shouldEqual Right(None, Seq.empty)\n\n      }\n\n    \"return an empty option and an empty list when passing only one element that not corresponds to the device id\" in\n      new CloudStorageProcessImplScope {\n\n        persistenceServices.getAndroidId returns TaskService(Task(Either.right(deviceId)))\n\n        val cloudStorageAnotherDevice =\n          generateCloudStorageDevice(cloudId = cloudId, minusDays = 1, deviceId = anotherDeviceId)\n\n        cloudStorageProcess\n          .prepareForActualDevice(mockApiClient, Seq(cloudStorageAnotherDevice))\n          .mustRight {\n            case (maybeUserDevice, devices) => maybeUserDevice must beNone\n          }\n\n      }\n\n    \"return the newest device when passing two elements that correspond with the device id\" in\n      new CloudStorageProcessImplScope {\n\n        persistenceServices.getAndroidId returns TaskService(Task(Either.right(deviceId)))\n\n        val cloudStorageDevice1 =\n          generateCloudStorageDevice(cloudId = cloudId, minusDays = 1, deviceId = deviceId)\n\n        val cloudStorageDevice2 =\n          generateCloudStorageDevice(cloudId = anotherCloudId, minusDays = 0, deviceId = deviceId)\n\n        val result = cloudStorageProcess\n          .prepareForActualDevice(mockApiClient, Seq(cloudStorageDevice1, cloudStorageDevice2))\n          .run\n        result shouldEqual Right(Some(cloudStorageDevice2), Seq(cloudStorageDevice1))\n\n      }\n\n    \"return the actual device and a sorted list when passing some elements and one of them correspond with the device id\" in\n      new CloudStorageProcessImplScope {\n\n        persistenceServices.getAndroidId returns TaskService(Task(Either.right(deviceId)))\n\n        val cloudStorageDeviceLast = generateCloudStorageDevice(\n          cloudId = anotherCloudId,\n          minusDays = 3,\n          deviceId = anotherDeviceId)\n\n        val cloudStorageDeviceFirst = generateCloudStorageDevice(\n          cloudId = anotherCloudId,\n          minusDays = 1,\n          deviceId = anotherDeviceId)\n\n        val cloudStorageDeviceMiddle =\n          generateCloudStorageDevice(anotherCloudId, 2, anotherDeviceId)\n\n        val allDevices = Seq(\n          cloudStorageDeviceMiddle,\n          cloudStorageDevice,\n          cloudStorageDeviceLast,\n          cloudStorageDeviceFirst)\n\n        val result = cloudStorageProcess.prepareForActualDevice(mockApiClient, allDevices).run\n        result shouldEqual Right(\n          Some(cloudStorageDevice),\n          Seq(cloudStorageDeviceFirst, cloudStorageDeviceMiddle, cloudStorageDeviceLast))\n\n      }\n\n    \"return a CloudStorageProcessException when the service returns an exception\" in\n      new CloudStorageProcessImplScope {\n\n        persistenceServices.getAndroidId returns TaskService(\n          Task(Either.left(androidIdNotFoundException)))\n        cloudStorageProcess\n          .prepareForActualDevice(mockApiClient, Seq.empty)\n          .mustLeft[CloudStorageProcessException]\n      }\n  }\n\n  \"getCloudStorageDevices\" should {\n\n    \"return a sequence of CloudStorageResource when the service returns a valid response\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getActiveUserId returns Some(activeUserId)\n        driveServices.listFiles(any, any) returns TaskService(\n          Task(Either.right(driveServiceFileSummarySeq)))\n        persistenceServices.findUserById(any) returns TaskService(Task(Either.right(Some(user))))\n\n        cloudStorageProcess.getCloudStorageDevices(mockApiClient).mustRight {\n          resultSeqCollection =>\n            resultSeqCollection.size shouldEqual driveServiceFileSummarySeq.size\n            resultSeqCollection.map(_.deviceName) shouldEqual driveServiceFileSummarySeq.map(\n              _.title)\n            resultSeqCollection.map(_.cloudId) shouldEqual driveServiceFileSummarySeq.map(_.uuid)\n        }\n\n      }\n\n    \"return an empty sequence when the service returns a valid empty sequence\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getActiveUserId returns Some(activeUserId)\n        driveServices.listFiles(any, any) returns TaskService(\n          Task(Either.right(driveServiceFileSummaryEmptySeq)))\n        persistenceServices.findUserById(any) returns TaskService(Task(Either.right(Some(user))))\n\n        val result = cloudStorageProcess.getCloudStorageDevices(mockApiClient).run\n        result shouldEqual Right(Seq.empty)\n      }\n\n    \"return a CloudStorageProcessException when the service return an exception\" in\n      new CloudStorageProcessImplScope {\n\n        driveServices.listFiles(any, any) returns TaskService(\n          Task(Either.left(driveServicesException)))\n        mockContextSupport.getActiveUserId returns Some(activeUserId)\n        persistenceServices.findUserById(any) returns TaskService(Task(Either.right(Some(user))))\n\n        cloudStorageProcess\n          .getCloudStorageDevices(mockApiClient)\n          .mustLeft[CloudStorageProcessException]\n      }\n\n    \"return a CloudStorageProcessException when there isn't a active user id\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getActiveUserId returns None\n\n        cloudStorageProcess\n          .getCloudStorageDevices(mockApiClient)\n          .mustLeft[CloudStorageProcessException]\n      }\n\n    \"return a CloudStorageProcessException when a user with this id doesn't exists\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getActiveUserId returns Some(activeUserId)\n        driveServices.listFiles(any, any) returns TaskService(\n          Task(Either.right(driveServiceFileSummaryEmptySeq)))\n        persistenceServices.findUserById(any) returns TaskService(Task(Either.right(None)))\n\n        cloudStorageProcess\n          .getCloudStorageDevices(mockApiClient)\n          .mustLeft[CloudStorageProcessException]\n      }\n\n    \"return a CloudStorageProcessException when the persistence service throws an exception\" in\n      new CloudStorageProcessImplScope {\n\n        persistenceServices.findUserById(any) returns TaskService(\n          Task(Either.left(persistenceServicesException)))\n        mockContextSupport.getActiveUserId returns Some(activeUserId)\n        driveServices.listFiles(any, any) returns TaskService(\n          Task(Either.right(driveServiceFileSummaryEmptySeq)))\n\n        cloudStorageProcess\n          .getCloudStorageDevices(mockApiClient)\n          .mustLeft[CloudStorageProcessException]\n      }\n\n  }\n\n  \"getCloudStorageDevice\" should {\n\n    \"return a valid CloudStorageDevice when the service returns a valid Json\" in\n      new CloudStorageProcessImplScope {\n\n        driveServices.readFile(any, any) returns TaskService(Task(Either.right(driveServiceFile)))\n\n        cloudStorageProcess.getCloudStorageDevice(mockApiClient, cloudId).mustRight { device =>\n          device.data.deviceId shouldEqual deviceId\n          device.data.deviceName shouldEqual deviceName\n          device.data.collections.size shouldEqual numCollections\n          device.data.collections.map(_.items.size) shouldEqual Seq.fill(numCollections)(\n            numItemsPerCollection)\n        }\n      }\n\n    \"return a CloudStorageProcessException when the service return a non valid Json\" in\n      new CloudStorageProcessImplScope {\n\n        driveServices.readFile(any, any) returns TaskService(\n          Task(Either.right(invalidDriveServiceFileJson)))\n        cloudStorageProcess\n          .getCloudStorageDevice(mockApiClient, cloudId)\n          .mustLeft[CloudStorageProcessException]\n      }\n\n    \"return a CloudStorageProcessException when the service return an exception\" in\n      new CloudStorageProcessImplScope {\n\n        driveServices.readFile(any, any) returns TaskService(\n          Task(Either.left(driveServicesException)))\n        cloudStorageProcess\n          .getCloudStorageDevice(mockApiClient, cloudId)\n          .mustLeft[CloudStorageProcessException]\n      }\n\n  }\n\n  \"createOrUpdateCloudStorageDevice\" should {\n\n    \"call to create file in Service with a valid Json and None for cloudId\" in\n      new CloudStorageProcessImplScope {\n\n        driveServices.createFile(\n          any,\n          anyString,\n          anArgThat[String, String](new JsonMatcher(validCloudStorageDeviceJson)),\n          anyString,\n          anyString,\n          anyString) returns TaskService(Task(Either.right(driveServiceFileSummary)))\n\n        val cloudStorageServiceData = generateCloudStorageDeviceData()\n\n        cloudStorageProcess\n          .createOrUpdateCloudStorageDevice(mockApiClient, None, cloudStorageServiceData)\n          .run\n      }.pendingUntilFixed\n\n    \"call to create file in Service with a valid Json and a cloudId\" in\n      new CloudStorageProcessImplScope {\n\n        driveServices.updateFile(any, anyString, anyString, anyString) returns TaskService(\n          Task(Either.right(driveServiceFileSummary)))\n\n        val cloudStorageServiceData = generateCloudStorageDeviceData()\n\n        cloudStorageProcess\n          .createOrUpdateCloudStorageDevice(mockApiClient, Some(cloudId), cloudStorageServiceData)\n          .run\n      }\n\n    \"return a CloudStorageProcessException when the service return an exception\" in\n      new CloudStorageProcessImplScope {\n\n        driveServices\n          .createFile(any, anyString, anyString, anyString, anyString, anyString) returns TaskService(\n          Task(Either.left(driveServicesException)))\n        cloudStorageProcess\n          .createOrUpdateCloudStorageDevice(mockApiClient, None, generateCloudStorageDeviceData())\n          .mustLeft[CloudStorageProcessException]\n      }\n\n  }\n\n  \"createOrUpdateActualCloudStorageDevice\" should {\n\n    \"call to create file in Service with a valid Json when the user doesn't has a cloudId\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getActiveUserId returns Some(activeUserId)\n        persistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(user.copy(deviceCloudId = None)))))\n        driveServices\n          .createFile(any, anyString, anyString, anyString, anyString, anyString) returns TaskService(\n          Task(Either.right(driveServiceFileSummary)))\n\n        val cloudStorageServiceData = generateCloudStorageDeviceData()\n\n        cloudStorageProcess\n          .createOrUpdateActualCloudStorageDevice(\n            mockApiClient,\n            cloudStorageServiceData.collections,\n            cloudStorageServiceData.moments getOrElse Seq.empty,\n            cloudStorageServiceData.dockApps getOrElse Seq.empty)\n          .run\n      }\n\n    \"call to update file in Service with a valid Json when the file does exists\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getActiveUserId returns Some(activeUserId)\n        persistenceServices.findUserById(any) returns TaskService(Task(Either.right(Some(user))))\n        driveServices.fileExists(any, any) returns TaskService(\n          Task(Either.right(Some(deviceName))))\n        driveServices.updateFile(any, anyString, anyString, anyString) returns TaskService(\n          Task(Either.right(driveServiceFileSummary)))\n\n        val cloudStorageServiceData = generateCloudStorageDeviceData()\n\n        cloudStorageProcess\n          .createOrUpdateActualCloudStorageDevice(\n            mockApiClient,\n            cloudStorageServiceData.collections,\n            cloudStorageServiceData.moments getOrElse Seq.empty,\n            cloudStorageServiceData.dockApps getOrElse Seq.empty)\n          .run\n      }\n\n    \"call to create file in Service with a valid Json when the file doesn't exists\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getActiveUserId returns Some(activeUserId)\n        persistenceServices.findUserById(any) returns TaskService(Task(Either.right(Some(user))))\n        driveServices.fileExists(any, any) returns TaskService(Task(Either.right(None)))\n        driveServices\n          .createFile(any, anyString, anyString, anyString, anyString, anyString) returns TaskService(\n          Task(Either.right(driveServiceFileSummary)))\n\n        val cloudStorageServiceData = generateCloudStorageDeviceData()\n\n        cloudStorageProcess\n          .createOrUpdateActualCloudStorageDevice(\n            mockApiClient,\n            cloudStorageServiceData.collections,\n            cloudStorageServiceData.moments getOrElse Seq.empty,\n            cloudStorageServiceData.dockApps getOrElse Seq.empty)\n          .run\n      }\n\n    \"return a CloudStorageProcessException when the user does exists\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getActiveUserId returns Some(activeUserId)\n        persistenceServices.findUserById(any) returns TaskService(Task(Either.right(None)))\n\n        val cloudStorageServiceData = generateCloudStorageDeviceData()\n\n        cloudStorageProcess\n          .createOrUpdateActualCloudStorageDevice(\n            mockApiClient,\n            cloudStorageServiceData.collections,\n            cloudStorageServiceData.moments getOrElse Seq.empty,\n            cloudStorageServiceData.dockApps getOrElse Seq.empty)\n          .mustLeft[CloudStorageProcessException]\n\n      }\n\n    \"return a CloudStorageProcessException when the drive service return an exception\" in\n      new CloudStorageProcessImplScope {\n\n        persistenceServices.getAndroidId returns TaskService(\n          Task(Either.left(androidIdNotFoundException)))\n        driveServices.updateFile(any, anyString, anyString, anyString) returns TaskService(\n          Task(Either.left(driveServicesException)))\n        mockContextSupport.getActiveUserId returns Some(activeUserId)\n        persistenceServices.findUserById(any) returns TaskService(Task(Either.right(Some(user))))\n\n        val cloudStorageServiceData = generateCloudStorageDeviceData()\n\n        cloudStorageProcess\n          .createOrUpdateActualCloudStorageDevice(\n            mockApiClient,\n            cloudStorageServiceData.collections,\n            cloudStorageServiceData.moments getOrElse Seq.empty,\n            cloudStorageServiceData.dockApps getOrElse Seq.empty)\n          .mustLeft[CloudStorageProcessException]\n      }\n\n    \"return a CloudStorageProcessException when the persistence service return an exception\" in\n      new CloudStorageProcessImplScope {\n\n        persistenceServices.getAndroidId returns TaskService(\n          Task(Either.left(androidIdNotFoundException)))\n        mockContextSupport.getActiveUserId returns Some(activeUserId)\n\n        val cloudStorageServiceData = generateCloudStorageDeviceData()\n\n        cloudStorageProcess\n          .createOrUpdateActualCloudStorageDevice(\n            mockApiClient,\n            cloudStorageServiceData.collections,\n            cloudStorageServiceData.moments getOrElse Seq.empty,\n            cloudStorageServiceData.dockApps getOrElse Seq.empty)\n          .mustLeft[CloudStorageProcessException]\n\n      }\n\n    \"return a CloudStorageProcessException when there isn't a active user id\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getActiveUserId returns None\n\n        val cloudStorageServiceData = generateCloudStorageDeviceData()\n\n        cloudStorageProcess\n          .createOrUpdateActualCloudStorageDevice(\n            mockApiClient,\n            cloudStorageServiceData.collections,\n            cloudStorageServiceData.moments getOrElse Seq.empty,\n            cloudStorageServiceData.dockApps getOrElse Seq.empty)\n          .mustLeft[CloudStorageProcessException]\n      }\n\n  }\n\n  \"deleteCloudStorageDeviceByAndroidId\" should {\n\n    \"return a valid response when the service finds the device\" in\n      new CloudStorageProcessImplScope {\n\n        driveServices.deleteFile(any, any) returns TaskService(Task(Either.right(Unit)))\n        val result = cloudStorageProcess.deleteCloudStorageDevice(mockApiClient, cloudId).run\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n    \"return a CloudStorageProcessException when the service returns an exception\" in\n      new CloudStorageProcessImplScope {\n\n        driveServices.deleteFile(any, any) returns TaskService(\n          Task(Either.left(driveServicesException)))\n        val result = cloudStorageProcess.deleteCloudStorageDevice(mockApiClient, cloudId).run\n        result must beAnInstanceOf[Left[DriveServicesException, _]]\n      }\n\n  }\n\n  \"getRawCloudStorageDevice\" should {\n\n    \"return a valid response when the service finds the device\" in\n      new CloudStorageProcessImplScope {\n\n        driveServices.readFile(any, any) returns TaskService(Task(Either.right(driveServiceFile)))\n        val expected = RawCloudStorageDevice(\n          cloudId = cloudId,\n          uuid = driveServiceFile.summary.uuid,\n          deviceId = driveServiceFile.summary.deviceId,\n          title = driveServiceFile.summary.title,\n          createdDate = driveServiceFile.summary.createdDate,\n          modifiedDate = driveServiceFile.summary.modifiedDate,\n          json = driveServiceFile.content)\n        val result = cloudStorageProcess.getRawCloudStorageDevice(mockApiClient, cloudId).run\n        result shouldEqual Right(expected)\n      }\n\n    \"return a CloudStorageProcessException when the service returns an exception\" in\n      new CloudStorageProcessImplScope {\n\n        driveServices.readFile(any, any) returns TaskService(\n          Task(Either.left(driveServicesException)))\n        cloudStorageProcess\n          .getRawCloudStorageDevice(mockApiClient, cloudId)\n          .mustLeft[CloudStorageProcessException]\n      }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/process/social/impl/SocialProfileProcessImplData.scala",
    "content": "package cards.nine.process.social.impl\n\nimport cards.nine.commons.test.data.UserTestData\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.services.plus.models.GooglePlusProfile\n\ntrait SocialProfileProcessImplData extends UserTestData {\n\n  val account  = \"example@domain.com\"\n  val clientId = \"fake-client-id\"\n\n  val googlePlusProfile =\n    GooglePlusProfile(name = Some(userName), avatarUrl = Some(avatar), coverUrl = Some(cover))\n\n}\n"
  },
  {
    "path": "modules/app/src/test/scala/cards/nine/process/social/impl/SocialProfileProcessImplSpecification.scala",
    "content": "package cards.nine.process.social.impl\n\nimport android.content.Context\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.process.social.{SocialProfileClientListener, SocialProfileProcessException}\nimport cards.nine.services.persistence.{PersistenceServiceException, PersistenceServices}\nimport cards.nine.services.plus.{GooglePlusServices, GooglePlusServicesException}\nimport cats.syntax.either._\nimport com.google.android.gms.common.api.GoogleApiClient\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\nimport scala.ref.WeakReference\n\ntrait SocialProfileProcessImplSpecification\n    extends TaskServiceSpecification\n    with Mockito\n    with SocialProfileProcessImplData {\n\n  trait CloudStorageProcessImplScope extends Scope {\n\n    implicit val mockContextSupport = mock[ContextSupport]\n\n    val mockContext = mock[Context]\n\n    val mockContextListener = mock[MyListener]\n\n    val googlePlusServicesException = GooglePlusServicesException(\"Irrelevant message\")\n\n    val googlePlusServices = mock[GooglePlusServices]\n\n    val persistenceServices = mock[PersistenceServices]\n\n    val mockApiClient = mock[GoogleApiClient]\n\n    val socialProfileProcess =\n      new SocialProfileProcessImpl(googlePlusServices, persistenceServices)\n\n  }\n\n  abstract class MyListener extends Context with SocialProfileClientListener\n\n}\n\nclass SocialProfileProcessImplSpec extends SocialProfileProcessImplSpecification {\n\n  \"createSocialProfileClient\" should {\n\n    \"return a valid response when the service returns a right response\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getOriginal returns new WeakReference(mockContextListener)\n        googlePlusServices.createGooglePlusClient(any, any)(any) returns TaskService(\n          Task(Right(mockApiClient)))\n        socialProfileProcess.createSocialProfileClient(clientId, account).run shouldEqual Right(\n          mockApiClient)\n      }\n\n    \"return a CloudStorageProcessException when the context doesn't implement SocialProfileClientListener\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getOriginal returns new WeakReference(mockContext)\n        googlePlusServices.createGooglePlusClient(any, any)(any) returns TaskService(\n          Task(Either.left(googlePlusServicesException)))\n        socialProfileProcess\n          .createSocialProfileClient(clientId, account)\n          .mustLeft[SocialProfileProcessException]\n      }\n\n    \"return a CloudStorageProcessException when the context doesn't exists\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getOriginal returns new WeakReference(null)\n        googlePlusServices.createGooglePlusClient(any, any)(any) returns TaskService(\n          Task(Either.left(googlePlusServicesException)))\n        socialProfileProcess\n          .createSocialProfileClient(clientId, account)\n          .mustLeft[SocialProfileProcessException]\n      }\n\n    \"return a CloudStorageProcessException when the service returns an exception\" in\n      new CloudStorageProcessImplScope {\n\n        mockContextSupport.getOriginal returns new WeakReference(mockContextListener)\n        googlePlusServices.createGooglePlusClient(any, any)(any) returns TaskService(\n          Task(Either.left(googlePlusServicesException)))\n        socialProfileProcess\n          .createSocialProfileClient(clientId, account)\n          .mustLeft[SocialProfileProcessException]\n      }\n\n  }\n\n  \"updateUserProfile\" should {\n\n    \"return an Answer and update profile in Persistence Service with the right params\" in\n      new CloudStorageProcessImplScope {\n\n        googlePlusServices.loadUserProfile(any) returns TaskService(\n          Task(Either.right(googlePlusProfile)))\n        mockContextSupport.getActiveUserId returns Some(userId)\n        persistenceServices.findUserById(any) returns TaskService(Task(Either.right(Some(user))))\n        persistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n\n        val result = socialProfileProcess.updateUserProfile(mockApiClient).run\n        result should beAnInstanceOf[Right[_, Unit]]\n\n        there was one(persistenceServices).findUserById(userId)\n\n        there was one(persistenceServices).updateUser(user)\n\n      }\n\n    \"return an Errata if the Persistence Service doesn't return an User\" in\n      new CloudStorageProcessImplScope {\n\n        googlePlusServices.loadUserProfile(any) returns TaskService(\n          Task(Either.right(googlePlusProfile)))\n        mockContextSupport.getActiveUserId returns Some(userId)\n        persistenceServices.findUserById(any) returns TaskService(Task(Either.right(None)))\n\n        val result = socialProfileProcess.updateUserProfile(mockApiClient).run\n        result must beAnInstanceOf[Left[SocialProfileProcessException, _]]\n\n        there was one(persistenceServices).findUserById(userId)\n\n      }\n\n    \"return an Errata with the SocialProfileProcessException if the Google Plus Services returns an Errata\" in\n      new CloudStorageProcessImplScope {\n\n        googlePlusServices.loadUserProfile(any) returns TaskService(\n          Task(Either.left(GooglePlusServicesException(\"Irrelevant message\"))))\n        socialProfileProcess\n          .updateUserProfile(mockApiClient)\n          .mustLeft[SocialProfileProcessException]\n      }\n\n    \"return an Errata with the SocialProfileProcessException if there is not an active user\" in\n      new CloudStorageProcessImplScope {\n\n        googlePlusServices.loadUserProfile(any) returns TaskService(\n          Task(Either.right(googlePlusProfile)))\n        mockContextSupport.getActiveUserId returns None\n\n        socialProfileProcess\n          .updateUserProfile(mockApiClient)\n          .mustLeft[SocialProfileProcessException]\n      }\n\n    \"return an Errata with the SocialProfileProcessException if the Persistence Service return an Errata in the findUserById method\" in\n      new CloudStorageProcessImplScope {\n\n        googlePlusServices.loadUserProfile(any) returns TaskService(\n          Task(Either.right(googlePlusProfile)))\n        mockContextSupport.getActiveUserId returns Some(userId)\n        persistenceServices.findUserById(any) returns TaskService(\n          Task(Either.left(PersistenceServiceException(\"Irrelevant message\"))))\n\n        socialProfileProcess\n          .updateUserProfile(mockApiClient)\n          .mustLeft[SocialProfileProcessException]\n\n        there was one(persistenceServices).findUserById(userId)\n      }\n\n    \"return an Errata with the SocialProfileProcessException if the Persistence Service return an Errata in the updateUser method\" in\n      new CloudStorageProcessImplScope {\n\n        googlePlusServices.loadUserProfile(any) returns TaskService(\n          Task(Either.right(googlePlusProfile)))\n        mockContextSupport.getActiveUserId returns Some(userId)\n        persistenceServices.findUserById(any) returns TaskService(Task(Either.right(Some(user))))\n        persistenceServices.updateUser(any) returns TaskService(\n          Task(Either.left(PersistenceServiceException(\"Irrelevant message\"))))\n\n        socialProfileProcess\n          .updateUserProfile(mockApiClient)\n          .mustLeft[SocialProfileProcessException]\n\n        there was one(persistenceServices).findUserById(userId)\n\n        there was one(persistenceServices).updateUser(user)\n      }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/commons/build.sbt",
    "content": "android.Plugin.androidBuild\n\nplatformTarget in Android := \"android-24\"\n"
  },
  {
    "path": "modules/commons/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      package=\"cards.nine.commons\"\n      android:versionCode=\"1\"\n      android:versionName=\"1.0\">\n    <uses-sdk\n        android:minSdkVersion=\"16\"\n        android:targetSdkVersion=\"24\" />\n\n    <application />\n</manifest>\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/CatchAll.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons\n\nimport cats.syntax.either._\nimport monix.eval.Task\n\nobject CatchAll {\n\n  def apply[E] = new CatchingAll[E]()\n\n  class CatchingAll[E] {\n    def apply[V](f: => V)(implicit converter: Throwable => E): Task[E Either V] =\n      Task(Either.catchNonFatal(f) leftMap converter)\n  }\n\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/NineCardExtensions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons\n\nimport cats.data.EitherT\nimport cards.nine.commons.services.TaskService._\nimport monix.eval.Task\nimport cats.syntax.either._\n\nimport scala.language.implicitConversions\n\nobject NineCardExtensions {\n\n  implicit class EitherTExtensions[A](r: EitherT[Task, NineCardException, A]) {\n\n    def resolve[E <: NineCardException](\n        implicit converter: Throwable => E): EitherT[Task, NineCardException, A] =\n      resolveLeft(e => Left(converter(e)))\n\n    def resolveIf[E <: NineCardException](\n        condition: Boolean,\n        ifNot: A): EitherT[Task, NineCardException, A] =\n      if (condition) {\n        r\n      } else {\n        EitherT(Task(Either.right(ifNot)))\n      }\n\n    def resolveLeftTo(result: A): EitherT[Task, NineCardException, A] =\n      resolveLeft((_) => Right(result))\n\n    def resolveLeft(mapLeft: NineCardException => Either[NineCardException, A]): EitherT[\n      Task,\n      NineCardException,\n      A] =\n      resolveSides((r) => Right(r), mapLeft)\n\n    def resolveRight[B](\n        mapRight: (A) => Either[NineCardException, B]): EitherT[Task, NineCardException, B] =\n      resolveSides(mapRight, (e) => Left(e))\n\n    def resolveAsOption: EitherT[Task, NineCardException, Option[A]] =\n      r.map(result => Option(result)).resolveLeftTo(None)\n\n    def resolveSides[B](\n        mapRight: (A) => Either[NineCardException, B],\n        mapLeft: NineCardException => Either[NineCardException, B] = (e: NineCardException) =>\n          Left(e)): EitherT[Task, NineCardException, B] = {\n      val task: Task[Either[NineCardException, A]] = r.value\n      val innerResult: Task[NineCardException Either B] = task.map {\n        case Right(v) => mapRight(v)\n        case Left(e)  => mapLeft(e)\n      }\n      EitherT(innerResult)\n    }\n\n  }\n\n  implicit class EitherTOptionExtensions[A](r: EitherT[Task, NineCardException, Option[A]]) {\n\n    case class EmptyException(message: String, cause: Option[Throwable] = None)\n        extends RuntimeException(message)\n        with NineCardException {\n      cause map initCause\n    }\n\n    def resolveOption(message: String): EitherT[Task, NineCardException, A] =\n      resolveOption(Option(message))\n\n    def resolveOption(message: Option[String] = None): EitherT[Task, NineCardException, A] =\n      r.resolveRight {\n        case Some(v) => Right(v)\n        case None    => Left(EmptyException(message getOrElse \"Value not found\"))\n      }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/contentresolver/ContentResolverWrapper.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.contentresolver\n\nimport java.util\n\nimport android.content.{ContentProviderOperation, ContentResolver, ContentValues}\nimport android.database.Cursor\nimport android.net.Uri\nimport android.net.Uri._\nimport cards.nine.commons.javaNull\n\ntrait ContentResolverWrapper {\n\n  def getCursor(\n      uri: Uri,\n      projection: Seq[String],\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\"): Cursor\n\n  def insert(uri: Uri, values: Map[String, Any], notificationUris: Seq[Uri] = Seq.empty): Int\n\n  def inserts(\n      authority: String,\n      uri: Uri,\n      allValues: Seq[Map[String, Any]],\n      notificationUris: Seq[Uri] = Seq.empty): Seq[Int]\n\n  def delete(\n      uri: Uri,\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      notificationUris: Seq[Uri] = Seq.empty): Int\n\n  def deleteById(\n      uri: Uri,\n      id: Int,\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      notificationUris: Seq[Uri] = Seq.empty): Int\n\n  def fetch[T](\n      uri: Uri,\n      projection: Seq[String],\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\")(f: (Cursor) => Option[T]): Option[T]\n\n  def fetchAll[T](\n      uri: Uri,\n      projection: Seq[String],\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\")(f: (Cursor) => Seq[T]): Seq[T]\n\n  def findById[T](\n      uri: Uri,\n      id: Int,\n      projection: Seq[String],\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\")(f: (Cursor) => Option[T]): Option[T]\n\n  def update(\n      uri: Uri,\n      values: Map[String, Any],\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      notificationUris: Seq[Uri] = Seq.empty): Int\n\n  def updateById(\n      uri: Uri,\n      id: Int,\n      values: Map[String, Any],\n      notificationUris: Seq[Uri] = Seq.empty): Int\n\n  def updateByIds(\n      authority: String,\n      uri: Uri,\n      idAndValues: Seq[(Int, Map[String, Any])],\n      notificationUris: Seq[Uri] = Seq.empty): Seq[Int]\n}\n\nclass ContentResolverWrapperImpl(contentResolver: ContentResolver) extends ContentResolverWrapper {\n\n  override def getCursor(\n      uri: Uri,\n      projection: Seq[String],\n      where: String,\n      whereParams: Seq[String],\n      orderBy: String): Cursor =\n    contentResolver.query(uri, projection.toArray, where, whereParams.toArray, orderBy)\n\n  override def insert(\n      uri: Uri,\n      values: Map[String, Any],\n      notificationUris: Seq[Uri] = Seq.empty): Int = {\n    val response = contentResolver.insert(uri, map2ContentValue(values))\n    val idString = response.getPathSegments.get(1)\n    notificationUris foreach (contentResolver.notifyChange(_, javaNull))\n    Integer.parseInt(idString)\n  }\n\n  override def inserts(\n      authority: String,\n      uri: Uri,\n      allValues: Seq[Map[String, Any]],\n      notificationUris: Seq[Uri] = Seq.empty): Seq[Int] = {\n\n    val operations = allValues map { values =>\n      ContentProviderOperation.newInsert(uri).withValues(map2ContentValue(values)).build()\n    }\n\n    import scala.collection.JavaConverters._\n    val result = contentResolver.applyBatch(authority, new util.ArrayList(operations.asJava))\n\n    notificationUris foreach (contentResolver.notifyChange(_, javaNull))\n\n    result.map(_.uri.getPathSegments.get(1).toInt)\n  }\n\n  override def update(\n      uri: Uri,\n      values: Map[String, Any],\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      notificationUris: Seq[Uri] = Seq.empty): Int = {\n    val updatedRowCount =\n      contentResolver.update(uri, map2ContentValue(values), where, whereParams.toArray)\n    notificationUris foreach (contentResolver.notifyChange(_, javaNull))\n    updatedRowCount\n  }\n\n  override def updateById(\n      uri: Uri,\n      id: Int,\n      values: Map[String, Any],\n      notificationUris: Seq[Uri] = Seq.empty): Int = {\n    val updatedRowCount = contentResolver\n      .update(withAppendedPath(uri, id.toString), map2ContentValue(values), \"\", Seq.empty.toArray)\n    notificationUris foreach (contentResolver.notifyChange(_, javaNull))\n    updatedRowCount\n  }\n\n  override def updateByIds(\n      authority: String,\n      uri: Uri,\n      idAndValues: Seq[(Int, Map[String, Any])],\n      notificationUris: Seq[Uri] = Seq.empty): Seq[Int] = {\n\n    val operations = idAndValues map {\n      case (id, values) =>\n        ContentProviderOperation\n          .newUpdate(withAppendedPath(uri, id.toString))\n          .withValues(map2ContentValue(values))\n          .build()\n    }\n\n    import scala.collection.JavaConverters._\n    val result = contentResolver.applyBatch(authority, new util.ArrayList(operations.asJava))\n\n    notificationUris foreach (contentResolver.notifyChange(_, javaNull))\n    result map (_.count.toInt)\n  }\n\n  override def delete(\n      uri: Uri,\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      notificationUris: Seq[Uri] = Seq.empty): Int = {\n    val deletedRowCount = contentResolver.delete(uri, where, whereParams.toArray)\n    notificationUris foreach (contentResolver.notifyChange(_, javaNull))\n    deletedRowCount\n  }\n\n  override def deleteById(\n      uri: Uri,\n      id: Int,\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      notificationUris: Seq[Uri] = Seq.empty): Int = {\n    val deletedRowCount =\n      contentResolver.delete(withAppendedPath(uri, id.toString), where, whereParams.toArray)\n    notificationUris foreach (contentResolver.notifyChange(_, javaNull))\n    deletedRowCount\n  }\n\n  override def fetch[T](\n      uri: Uri,\n      projection: Seq[String],\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\")(f: (Cursor) => Option[T]): Option[T] =\n    Option(contentResolver.query(uri, projection.toArray, where, whereParams.toArray, orderBy)) match {\n      case None         => None\n      case Some(cursor) => f(cursor)\n    }\n\n  override def fetchAll[T](\n      uri: Uri,\n      projection: Seq[String],\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\")(f: (Cursor) => Seq[T]): Seq[T] =\n    Option(contentResolver.query(uri, projection.toArray, where, whereParams.toArray, orderBy)) match {\n      case None         => Seq.empty\n      case Some(cursor) => f(cursor)\n    }\n\n  override def findById[T](\n      uri: Uri,\n      id: Int,\n      projection: Seq[String],\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\")(f: (Cursor) => Option[T]): Option[T] =\n    Option(\n      contentResolver.query(\n        withAppendedPath(uri, id.toString),\n        projection.toArray,\n        where,\n        whereParams.toArray,\n        orderBy)) match {\n      case None         => None\n      case Some(cursor) => f(cursor)\n    }\n\n  def map2ContentValue(values: Map[String, Any]) = {\n    val contentValues = new ContentValues()\n\n    values foreach {\n      case (key, `javaNull`)         => contentValues.putNull(key)\n      case (key, value: Array[Byte]) => contentValues.put(key, value)\n      case (key, value: Byte)        => contentValues.put(key, value.asInstanceOf[java.lang.Byte])\n      case (key, value: Boolean)     => contentValues.put(key, value)\n      case (key, value: Float)       => contentValues.put(key, value.asInstanceOf[java.lang.Float])\n      case (key, value: Double)      => contentValues.put(key, value)\n      case (key, value: Int)         => contentValues.put(key, value.asInstanceOf[java.lang.Integer])\n      case (key, value: Long)        => contentValues.put(key, value.asInstanceOf[java.lang.Long])\n      case (key, value: Short)       => contentValues.put(key, value.asInstanceOf[java.lang.Short])\n      case (key, value: String)      => contentValues.put(key, value)\n    }\n\n    contentValues\n  }\n\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/contentresolver/Conversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.contentresolver\n\nimport android.database.Cursor\n\nimport scala.annotation.tailrec\n\nobject Conversions {\n\n  def getEntityFromCursor[T](conversionFunction: Cursor => T)(cursor: Cursor): Option[T] = {\n    val entity = cursor.moveToFirst() match {\n      case true => Some(conversionFunction(cursor))\n      case _    => None\n    }\n\n    cursor.close()\n    entity\n  }\n\n  def getListFromCursor[T](conversionFunction: Cursor => T)(cursor: Cursor): Seq[T] = {\n    @tailrec\n    def getListFromEntityLoop(cursor: Cursor, result: Seq[T]): Seq[T] =\n      cursor match {\n        case validCursor if validCursor.isAfterLast => result\n        case _ =>\n          val entity = conversionFunction(cursor)\n          cursor.moveToNext\n          getListFromEntityLoop(cursor, result :+ entity)\n      }\n\n    val list = cursor.moveToFirst() match {\n      case true => getListFromEntityLoop(cursor, Seq.empty[T])\n      case _    => Seq.empty[T]\n    }\n\n    cursor.close()\n    list\n  }\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/contentresolver/NotificationUri.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.contentresolver\n\nobject NotificationUri {\n\n  val authorityPart = \"com.fortysevendeg.ninecardslauncher\"\n\n  val contentPrefix = \"notification://\"\n\n  val baseUriNotificationString = s\"$contentPrefix$authorityPart\"\n\n  val appUriPath = \"app\"\n\n  val cardUriPath = \"card\"\n\n  val collectionUriPath = \"collection\"\n\n  val dockAppUriPath = \"dockApp\"\n\n  val momentUriPath = \"moment\"\n\n  val userUriPath = \"user\"\n\n  val widgetUriPath = \"widget\"\n\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/contentresolver/UriCreator.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.contentresolver\n\nimport android.net.Uri\n\nclass UriCreator {\n\n  def parse(uriString: String): Uri = Uri.parse(uriString)\n\n  def withAppendedPath(uri: Uri, path: String) = Uri.withAppendedPath(uri, path)\n\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/contexts/ContextSupport.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.contexts\n\nimport java.io.File\n\nimport android.accounts.AccountManager\nimport android.app.{Activity, AlarmManager, Application}\nimport android.content.{ContentResolver, Context, Intent, SharedPreferences}\nimport android.content.pm.PackageManager\nimport android.content.res.{AssetManager, Resources}\n\nimport scala.ref.WeakReference\n\ntrait ContextSupport {\n  def application: Application\n  def context: Context\n  def getOriginal: WeakReference[Context]\n  def getPackageManager: PackageManager\n  def getResources: Resources\n  def getContentResolver: ContentResolver\n  def getFilesDir: File\n  def getAppIconsDir: File\n  def getAssets: AssetManager\n  def getPackageName: String\n  def getSharedPreferences: SharedPreferences\n  def getActiveUserId: Option[Int]\n  def setActiveUserId(id: Int): Unit\n  def addBluetoothDevice(device: String): Unit\n  def removeBluetoothDevice(device: String): Unit\n  def clearBluetoothDevices(): Unit\n  def getBluetoothDevicesConnected: Set[String]\n  def getAccountManager: AccountManager\n  def createIntent(classOf: Class[_]): Intent\n  def getAlarmManager: Option[AlarmManager]\n}\n\ntrait ActivityContextSupport extends ContextSupport {\n  def getActivity: Option[Activity]\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/ops/ColorOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.ops\n\nimport android.graphics.Color\n\nobject ColorOps {\n\n  implicit class IntColors(color: Int) {\n\n    def light(ratio: Float = 0.1f) = {\n      val colorHsv = Array(0f, 0f, 0f)\n      Color.colorToHSV(color, colorHsv)\n      colorHsv.update(2, math.min(colorHsv(2) + ratio, 1))\n      Color.HSVToColor(colorHsv)\n    }\n\n    def dark(ratio: Float = 0.1f) = {\n      val colorHsv = Array(0f, 0f, 0f)\n      Color.colorToHSV(color, colorHsv)\n      colorHsv.update(2, math.max(colorHsv(2) - ratio, 0))\n      Color.HSVToColor(colorHsv)\n    }\n\n    def alpha(alpha: Float): Int =\n      Color.argb((255 * alpha).toInt, Color.red(color), Color.green(color), Color.blue(color))\n\n    def colorToString: String = s\"#${0xFFFFFF & color}\"\n\n  }\n\n  implicit class InterpolateColors(colors: (Int, Int)) {\n\n    def interpolateColors(fraction: Float): Int = {\n      val startValue: Int = colors._1\n      val endValue: Int   = colors._2\n      val startInt: Int   = startValue\n      val startA: Int     = (startInt >> 24) & 0xff\n      val startR: Int     = (startInt >> 16) & 0xff\n      val startG: Int     = (startInt >> 8) & 0xff\n      val startB: Int     = startInt & 0xff\n      val endInt: Int     = endValue\n      val endA: Int       = (endInt >> 24) & 0xff\n      val endR: Int       = (endInt >> 16) & 0xff\n      val endG: Int       = (endInt >> 8) & 0xff\n      val endB: Int       = endInt & 0xff\n      ((startA + (fraction * (endA - startA)).toInt) << 24) |\n        ((startR + (fraction * (endR - startR)).toInt) << 16) |\n        (startG + (fraction * (endG - startG)).toInt) << 8 |\n        (startB + (fraction * (endB - startB)).toInt)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/ops/SeqOps.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.ops\n\nobject SeqOps {\n\n  implicit class SeqCursor[T](seq: Seq[T]) {\n    def reorder(from: Int, to: Int) = {\n      val range1 = math.min(from, to)\n      val range2 = math.max(from, to)\n\n      val header       = seq.take(range1)\n      val tail         = seq.drop(range2 + 1)\n      val updatedRange = reorderRange(from, to)\n\n      header ++ updatedRange ++ tail\n    }\n\n    def reorderRange(from: Int, to: Int) = {\n      val range1 = math.min(from, to)\n      val range2 = math.max(from, to)\n\n      val range = seq.slice(range1, range2 + 1)\n      val updatedRange = if (from < to) {\n        range.drop(1) ++ range.take(1)\n      } else {\n        range.takeRight(1) ++ range.dropRight(1)\n      }\n      updatedRange\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/package.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine\n\npackage object commons {\n\n  val javaNull = None.orNull\n\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/services/package.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons\n\nimport cats.data.EitherT\nimport cats.syntax.either._\nimport monix.cats.MonixToCatsConversions\nimport monix.eval.Task\n\nimport scala.language.{higherKinds, implicitConversions}\n\npackage object services {\n\n  object TaskService extends MonixToCatsConversions {\n\n    trait NineCardException extends RuntimeException {\n      def message: String\n      def cause: Option[Throwable]\n    }\n\n    type TaskService[A] = EitherT[Task, NineCardException, A]\n\n    def apply[A](f: Task[NineCardException Either A]): TaskService[A] =\n      EitherT[Task, NineCardException, A](f)\n\n    def empty: TaskService[Unit] = EitherT(Task(Either.right((): Unit)))\n\n    def left[A](ex: NineCardException): TaskService[A] = EitherT(Task(Either.left(ex)))\n\n    def right[A](value: A): TaskService[A] = EitherT(Task(Either.right(value)))\n\n  }\n\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/utils/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.utils\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class AssetException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsAssetException {\n  implicit def assetException = (t: Throwable) => AssetException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/utils/FileUtils.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.utils\n\nimport java.io._\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.utils.impl.StreamWrapperImpl\n\nimport scala.util.Try\nimport scala.util.control.Exception._\n\nclass FileUtils(streamWrapper: StreamWrapper = new StreamWrapperImpl)\n    extends ImplicitsAssetException {\n\n  def readFile(filename: String)(implicit context: ContextSupport): Try[String] =\n    Try {\n      withResource[InputStream, String](streamWrapper.openAssetsFile(filename)) { stream =>\n        {\n          streamWrapper.makeStringFromInputStream(stream)\n        }\n      }\n    }\n\n  private[this] def withResource[C <: Closeable, R](closeable: C)(f: C => R) =\n    allCatch.andFinally(closeable.close())(f(closeable))\n\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/utils/StreamWrapper.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.utils\n\nimport java.io._\nimport java.util.zip.{GZIPInputStream, GZIPOutputStream}\n\nimport cards.nine.commons.contexts.ContextSupport\n\ntrait StreamWrapper {\n\n  def openAssetsFile(filename: String)(implicit context: ContextSupport): InputStream\n\n  def makeStringFromInputStream(stream: InputStream): String\n\n  def createFileInputStream(file: File): FileInputStream\n  def createFileOutputStream(file: File): FileOutputStream\n\n  def createGZIPInputStream(fileInputStream: FileInputStream): GZIPInputStream\n  def createGZIPOutputStream(fileOutputStream: FileOutputStream): GZIPOutputStream\n\n  def createObjectInputStream(gzipInputStream: GZIPInputStream): ObjectInputStream\n  def createObjectOutputStream(gzipOutputStream: GZIPOutputStream): ObjectOutputStream\n\n  def readObjectAsInstance[T](objectInputStream: ObjectInputStream): T\n  def writeObject[T](out: ObjectOutputStream, obj: T): Unit\n\n}\n"
  },
  {
    "path": "modules/commons/src/main/scala/cards/nine/commons/utils/impl/StreamWrapperImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.utils.impl\n\nimport java.io._\nimport java.util.zip.{GZIPInputStream, GZIPOutputStream}\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.utils.StreamWrapper\n\nimport scala.io.Source\n\nclass StreamWrapperImpl extends StreamWrapper {\n\n  def openAssetsFile(filename: String)(implicit context: ContextSupport) =\n    context.getAssets.open(filename)\n\n  def makeStringFromInputStream(stream: InputStream): String =\n    Source.fromInputStream(stream, \"UTF-8\").mkString\n\n  def createFileInputStream(file: File)  = new FileInputStream(file)\n  def createFileOutputStream(file: File) = new FileOutputStream(file)\n\n  def createGZIPInputStream(fileInputStream: FileInputStream) =\n    new GZIPInputStream(fileInputStream)\n  def createGZIPOutputStream(fileOutputStream: FileOutputStream) =\n    new GZIPOutputStream(fileOutputStream)\n\n  def createObjectInputStream(gzipInputStream: GZIPInputStream) =\n    new ObjectInputStream(gzipInputStream)\n  def createObjectOutputStream(gzipOutputStream: GZIPOutputStream) =\n    new ObjectOutputStream(gzipOutputStream)\n\n  def readObjectAsInstance[T](objectInputStream: ObjectInputStream): T =\n    objectInputStream.readObject.asInstanceOf[T]\n  def writeObject[T](out: ObjectOutputStream, obj: T): Unit = out.writeObject(obj)\n\n}\n"
  },
  {
    "path": "modules/commons/src/test/scala/cards/nine/commons/utils/FileUtilsData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.utils\n\ntrait FileUtilsData {\n\n  val packageName = \"cards.nine.test\"\n\n  val className = \"ClassNameExample\"\n\n  val fileName = String.format(\n    \"%s_%s\",\n    packageName.toLowerCase.replace(\".\", \"_\"),\n    className.toLowerCase.replace(\".\", \"_\"))\n\n  val fileFolder = \"/file/example\"\n\n  val resultFilePath = s\"$fileFolder/$fileName\"\n\n  val fileJson = \"\"\"{\n                      \"packageName\": \"cards.nine.test\",\n                      \"className\": \"ClassNameExample\",\n                      \"name\": \"Sample Name\"\n                     }\"\"\"\n\n  val sourceString = \"Source String\"\n}\n"
  },
  {
    "path": "modules/commons/src/test/scala/cards/nine/commons/utils/FileUtilsSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.utils\n\nimport java.io._\n\nimport cards.nine.commons.contexts.ContextSupport\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\nimport scala.util.Success\n\ntrait FileUtilsSpecification extends Specification with Mockito {\n\n  trait FileUtilsScope extends Scope with FileUtilsData {\n\n    val mockContextSupport = mock[ContextSupport]\n    val mockStreamWrapper  = mock[StreamWrapper]\n    val mockInputStream    = mock[InputStream]\n\n    val fileUtils = new FileUtils(mockStreamWrapper)\n\n  }\n\n  trait ValidUtilsScope { self: FileUtilsScope =>\n\n    mockStreamWrapper.openAssetsFile(fileName)(mockContextSupport) returns mockInputStream\n    mockStreamWrapper.makeStringFromInputStream(mockInputStream) returns fileJson\n\n  }\n\n  trait ErrorUtilsScope { self: FileUtilsScope =>\n\n    mockStreamWrapper.openAssetsFile(fileName)(mockContextSupport) throws new RuntimeException(\"\")\n\n  }\n\n}\n\nclass FileUtilsSpec extends FileUtilsSpecification {\n\n  \"File Utils\" should {\n\n    \"returns a json string when a valid fileName is provided\" in\n      new FileUtilsScope with ValidUtilsScope {\n        val result = fileUtils.readFile(fileName)(mockContextSupport)\n        result mustEqual Success(fileJson)\n      }\n\n    \"returns an Exception when the file can't be opened\" in\n      new FileUtilsScope with ErrorUtilsScope {\n        val result = fileUtils.readFile(fileName)(mockContextSupport)\n        result must beFailedTry\n      }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/commons/src/test/scala/cards/nine/commons/utils/impl/StreamWrapperImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.commons.utils.impl\n\nimport java.io.{ByteArrayInputStream, InputStream}\n\nimport android.content.res.AssetManager\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.utils.{FileUtilsData, StreamWrapper}\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait StreamWrapperSpecification extends Specification with Mockito {\n\n  trait StreamWrapperScope extends Scope with FileUtilsData {\n\n    val mockContextSupport           = mock[ContextSupport]\n    val streamWrapper: StreamWrapper = new StreamWrapperImpl\n    val mockInputStream              = mock[InputStream]\n\n  }\n\n  trait OpenAssetsScope { self: StreamWrapperScope =>\n\n    val mockAssetManager = mock[AssetManager]\n\n    mockContextSupport.getAssets returns mockAssetManager\n    mockAssetManager.open(fileName) returns mockInputStream\n\n  }\n\n  trait MakeStringScope { self: StreamWrapperScope =>\n\n    val inputStream = new ByteArrayInputStream(sourceString.getBytes)\n  }\n\n}\n\nclass StreamWrapperImplSpec extends StreamWrapperSpecification {\n\n  \"Stream Wrapper\" should {\n\n    \"return an InputStream when a filename is provided\" in {\n      new StreamWrapperScope with OpenAssetsScope {\n        val result = streamWrapper.openAssetsFile(fileName)(mockContextSupport)\n        result mustEqual mockInputStream\n      }\n    }\n\n    \"return a String when an InputStream is provided\" in {\n      new StreamWrapperScope with MakeStringScope {\n        val result = streamWrapper.makeStringFromInputStream(inputStream)\n        result mustEqual sourceString\n      }\n    }\n\n  }\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/TaskServiceTestOps.scala",
    "content": "package cards.nine.commons.test\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.{NineCardException, _}\nimport monix.eval.Task\nimport monix.execution.Scheduler.Implicits.global\nimport org.specs2.matcher.MatchResult\nimport org.specs2.mutable.Specification\n\nimport scala.concurrent.Await\nimport scala.concurrent.duration._\nimport scala.reflect.ClassTag\n\nobject TaskServiceTestOps {\n\n  implicit class TaskServiceTestAwait[A](t: Task[NineCardException Either A]) {\n\n    def run: NineCardException Either A = Await.result(t.runAsync, 10.seconds)\n\n  }\n\n}\n\ntrait TaskServiceSpecification extends Specification {\n\n  def serviceRight[A <: NineCardException, B](v: B) = TaskService(Task(Right[A, B](v)))\n\n  def serviceLeft[A <: NineCardException, B](e: A) = TaskService(Task(Left[A, B](e)))\n\n  implicit class TaskServiceTestAwait[B](service: TaskService[B]) {\n\n    def run: NineCardException Either B = Await.result(service.value.runAsync, Duration.Inf)\n\n    def mustLeft[A <: NineCardException](implicit classTag: ClassTag[A]): Unit =\n      service.run must beLike {\n        case Left(e) => e must beAnInstanceOf[A]\n      }\n\n    def mustRight(f: (B) => MatchResult[_]): Unit =\n      service.run must beLike {\n        case Right(v) => f(v)\n      }\n\n    def mustRightUnit: Unit = mustRight(_ shouldEqual ((): Unit))\n\n    def mustRightNone: Unit = mustRight(_ shouldEqual None)\n  }\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/ApiTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.commons.test.data.ApiValues._\nimport cards.nine.commons.test.data.AppWidgetValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.MomentValues._\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.models._\nimport cards.nine.models.types.{NineCardsCategory, NineCardsMoment}\n\ntrait ApiTestData extends ApplicationTestData with AppWidgetTestData {\n\n  implicit val requestConfig = RequestConfig(\n    apiKey = apiKey,\n    sessionToken = sessionToken,\n    androidId = androidId,\n    marketToken = Option(marketToken))\n\n  val loginResponse = LoginResponse(apiKey = apiKey, sessionToken = sessionToken)\n\n  val awarenessLocation = Location(\n    latitude = latitude,\n    longitude = longitude,\n    countryCode = Option(countryCode),\n    countryName = Option(countryName),\n    addressLines = Seq(street, city, postalCode))\n\n  def categorizedPackage(num: Int = 0) =\n    CategorizedPackage(packageName = apiPackageName + num, category = Option(category))\n\n  val categorizedPackage: CategorizedPackage = categorizedPackage(0)\n  val seqCategorizedPackage: Seq[CategorizedPackage] =\n    Seq(categorizedPackage(0), categorizedPackage(1), categorizedPackage(2))\n\n  val categorizedDetailPackages = seqApplication map { app =>\n    CategorizedDetailPackage(\n      packageName = app.packageName,\n      title = app.name,\n      category = Option(app.category),\n      icon = apiIcon,\n      free = free,\n      downloads = downloads,\n      stars = stars)\n  }\n\n  val seqCategoryAndPackages: Seq[(NineCardsCategory, Seq[String])] =\n    (seqApplication map (app => (app.category, app.packageName)))\n      .groupBy(_._1)\n      .mapValues(_.map(_._2))\n      .toSeq\n\n  val seqRankApps: Seq[RankApps] = seqCategoryAndPackages map { item =>\n    RankApps(category = item._1, packages = item._2)\n  }\n\n  val seqPackagesByCategory =\n    seqCategoryAndPackages map { item =>\n      PackagesByCategory(category = item._1, packages = item._2)\n    }\n\n  def notCategorizedPackage(num: Int = 0) =\n    NotCategorizedPackage(\n      packageName = apiPackageName + num,\n      title = apiTitle + num,\n      downloads = downloads,\n      icon = Option(apiIcon),\n      stars = stars,\n      free = free,\n      screenshots = screenshots)\n\n  val notCategorizedPackage: NotCategorizedPackage = notCategorizedPackage(0)\n  val seqNotCategorizedPackage: Seq[NotCategorizedPackage] =\n    Seq(notCategorizedPackage(0), notCategorizedPackage(1), notCategorizedPackage(2))\n\n  def rankAppsByMoment(num: Int = 0) =\n    RankAppsByMoment(\n      moment = NineCardsMoment(momentTypeSeq(num)),\n      packages = Seq(apiPackages(num)))\n\n  val seqRankAppsByMoment: Seq[RankAppsByMoment] =\n    Seq(rankAppsByMoment(0), rankAppsByMoment(1), rankAppsByMoment(2))\n\n  def packagesByMoment(num: Int = 0) =\n    PackagesByMoment(\n      moment = NineCardsMoment(momentTypeSeq(num)),\n      packages = Seq(apiPackages(num)))\n\n  val seqPackagesByMoment: Seq[PackagesByMoment] =\n    Seq(packagesByMoment(0), packagesByMoment(1), packagesByMoment(2))\n\n  def rankWidget(num: Int = 0) =\n    RankWidget(packageName = appWidgetPackageName + num, className = appWidgetClassName + num)\n\n  val seqRankWidget: Seq[RankWidget] =\n    Seq(rankWidget(0), rankWidget(1), rankWidget(2))\n\n  def rankWidgetsByMoment(num: Int = 0) =\n    RankWidgetsByMoment(\n      moment = NineCardsMoment(momentTypeSeq(num)),\n      widgets = Seq(seqRankWidget(num)))\n\n  val seqRankWidgetsByMoment: Seq[RankWidgetsByMoment] =\n    Seq(rankWidgetsByMoment(0), rankWidgetsByMoment(1), rankWidgetsByMoment(2))\n\n  def widgetsByMoment(num: Int = 0) =\n    WidgetsByMoment(moment = NineCardsMoment(momentTypeSeq(num)), widgets = Seq(seqAppWidget(num)))\n\n  val seqWidgetsByMoment: Seq[WidgetsByMoment] =\n    Seq(widgetsByMoment(0), widgetsByMoment(1), widgetsByMoment(2))\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/ApiV1TestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.commons.test.data.ApiV1Values._\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.SharedCollectionValues._\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.models._\n\ntrait ApiV1TestData {\n\n  def requestConfigV1(num: Int = 0) =\n    RequestConfigV1(\n      deviceId = userDeviceId + num,\n      token = sessionToken,\n      marketToken = Option(marketToken))\n\n  implicit val requestConfigV1: RequestConfigV1 = requestConfigV1(0)\n\n  def device(num: Int = 0) =\n    Device(\n      name = userDeviceName + num,\n      deviceId = userDeviceId + num,\n      secretToken = marketToken,\n      permissions = permissions)\n\n  val device: Device         = device(0)\n  val seqDevice: Seq[Device] = Seq(device(0), device(1), device(2))\n\n  val loginResponseV1 = LoginResponseV1(\n    userId = Option(userId.toString),\n    sessionToken = Option(sessionToken),\n    email = Option(email),\n    devices = seqDevice)\n\n  def userV1CollectionItem(num: Int = 0) =\n    UserV1CollectionItem(\n      itemType = itemType,\n      title = title + num,\n      intent = intent,\n      categories = Option(Seq(category, anotherCategory)))\n\n  val seqUserV1CollectionItem: Seq[UserV1CollectionItem] =\n    Seq(userV1CollectionItem(0), userV1CollectionItem(1), userV1CollectionItem(2))\n\n  def userV1Collection(num: Int = 0) =\n    UserV1Collection(\n      name = collectionName,\n      originalSharedCollectionId = Option(originalSharedCollectionId),\n      sharedCollectionId = Option(sharedCollectionId),\n      sharedCollectionSubscribed = Option(sharedCollectionSubscribed),\n      items = seqUserV1CollectionItem,\n      collectionType = collectionType,\n      constrains = constrains,\n      wifi = wifiSeq,\n      occurrence = occurrence,\n      icon = apiV1CollectionIcon,\n      category = Option(category))\n\n  val seqUserV1Collection: Seq[UserV1Collection] =\n    Seq(userV1Collection(0), userV1Collection(1), userV1Collection(2))\n\n  def userV1Device(num: Int = 0) =\n    UserV1Device(\n      deviceId = deviceIdPrefix + num,\n      deviceName = userDeviceName + num,\n      collections = seqUserV1Collection)\n\n  val seqUserV1Device: Seq[UserV1Device] = Seq(userV1Device(0), userV1Device(1), userV1Device(2))\n\n  def userV1(num: Int = 0) =\n    UserV1(\n      _id = userV1Id,\n      email = email,\n      plusProfile = UserV1PlusProfile(\n        displayName = displayName,\n        profileImage = UserV1ProfileImage(imageType = 0, imageUrl = imageUrl)),\n      devices = seqUserV1Device,\n      status = UserV1StatusInfo(\n        products = products,\n        friendsReferred = friendsReferred,\n        themesShared = themesShared,\n        collectionsShared = collectionsShared,\n        customCollections = customCollections,\n        earlyAdopter = earlyAdopter,\n        communityMember = communityMember,\n        joinedThrough = joinedThrough,\n        tester = tester))\n\n  val userV1: UserV1         = userV1(0)\n  val seqUserV1: Seq[UserV1] = Seq(userV1(0), userV1(1), userV1(2))\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/AppWidgetTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.commons.test.data.AppWidgetValues._\nimport cards.nine.commons.test.data.ApplicationValues._\nimport cards.nine.models.{AppWidget, AppsWithWidgets}\nimport cards.nine.models.types.WidgetResizeMode\n\ntrait AppWidgetTestData {\n\n  def appWidget(num: Int = 0) =\n    AppWidget(\n      userHashCode = Option(userHashCode),\n      autoAdvanceViewId = autoAdvanceViewId,\n      initialLayout = initialLayout,\n      minHeight = minHeight,\n      minResizeHeight = minResizeHeight,\n      minResizeWidth = minResizeWidth,\n      minWidth = minWidth,\n      className = appWidgetClassName + num,\n      packageName = appWidgetPackageName + num,\n      resizeMode = WidgetResizeMode(resizeMode),\n      updatePeriodMillis = updatePeriodMillis,\n      label = label,\n      preview = preview)\n\n  val appWidget: AppWidget         = appWidget(0)\n  val seqAppWidget: Seq[AppWidget] = Seq(appWidget(0), appWidget(1), appWidget(2))\n\n  def appsWithWidgets(num: Int = 0) =\n    AppsWithWidgets(\n      packageName = applicationPackageName + num,\n      name = applicationName + num,\n      widgets = seqAppWidget)\n\n  val seqAppsWithWidgets: Seq[AppsWithWidgets] =\n    Seq(appsWithWidgets(0), appsWithWidgets(1), appsWithWidgets(2))\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/ApplicationTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.commons.test.data.ApplicationValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.models.{Application, ApplicationData}\n\ntrait ApplicationTestData {\n\n  def application(num: Int = 0) =\n    Application(\n      id = applicationId + num,\n      name = applicationName + num,\n      packageName = applicationPackageName + num,\n      className = applicationClassName + num,\n      category = category,\n      dateInstalled = dateInstalled,\n      dateUpdated = dateUpdated,\n      version = version,\n      installedFromGooglePlay = installedFromGooglePlay)\n\n  val application: Application         = application(0)\n  val seqApplication: Seq[Application] = Seq(application(0), application(1), application(2))\n\n  val applicationData: ApplicationData         = application.toData\n  val seqApplicationData: Seq[ApplicationData] = seqApplication map (_.toData)\n\n  val seqApplicationDataPackages: Seq[String] = seqApplicationData.map(_.packageName)\n  val setApplicationDataPackages: Set[String] = seqApplicationDataPackages.toSet\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/CardTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.commons.test.data.CardValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.models.types.CardType\nimport cards.nine.models.{Card, CardData, NineCardsIntentConversions}\n\ntrait CardTestData extends NineCardsIntentConversions {\n\n  def card(num: Int = 0) =\n    Card(\n      id = cardId + num,\n      position = cardPosition + num,\n      term = term,\n      packageName = Option(cardPackageName + num),\n      cardType = CardType(cardType),\n      intent = jsonToNineCardIntent(intent),\n      imagePath = Option(cardImagePath),\n      notification = Option(notification))\n\n  val card: Card         = card(0)\n  val seqCard: Seq[Card] = Seq(card(0), card(1), card(2))\n\n  val cardData: CardData         = card.toData\n  val seqCardData: Seq[CardData] = seqCard map (_.toData)\n\n  val cardPackageSeq: Seq[String]     = seqCard flatMap (_.packageName)\n  val cardDataPackageSeq: Seq[String] = seqCardData flatMap (_.packageName)\n\n  val cardIdSeq: Seq[Int] = seqCard map (_.id)\n\n  val cardPackageSet: Set[String] = cardPackageSeq.toSet\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/CloudStorageTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.models._\nimport cards.nine.models.types._\nimport org.joda.time.DateTime\nimport cards.nine.commons.test.data.CloudStorageValues._\nimport play.api.libs.json.Json\nimport cards.nine.models.NineCardsIntentImplicits._\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.DockAppValues._\n\ntrait CloudStorageTestData extends UserTestData {\n\n  def generateCloudStorageDeviceData(deviceId: String = deviceId) =\n    CloudStorageDeviceData(\n      deviceId,\n      deviceName,\n      documentVersion,\n      generateCollections(numCollections, numItemsPerCollection),\n      Some(generateMoments(numMoments, numTimeSlot)),\n      Some(generateDockApps(numDockApps)))\n\n  def generateCloudStorageDevice(\n      cloudId: String = cloudId,\n      minusDays: Int = 0,\n      deviceId: String = deviceId) =\n    CloudStorageDevice(\n      cloudId,\n      createdDate = DateTime.now().minusDays(minusDays).toDate,\n      modifiedDate = DateTime.now().minusDays(minusDays).toDate,\n      data = generateCloudStorageDeviceData(deviceId))\n\n  def generateCloudStorageDeviceSummary(\n      cloudId: String = cloudId,\n      minusDays: Int = 0,\n      deviceId: String = deviceId,\n      deviceName: String = deviceName,\n      currentDevice: Boolean = true) =\n    CloudStorageDeviceSummary(\n      cloudId = cloudId,\n      deviceId = Option(deviceId),\n      deviceName = deviceName,\n      createdDate = DateTime.now().minusDays(minusDays).toDate,\n      modifiedDate = DateTime.now().minusDays(minusDays).toDate,\n      currentDevice = currentDevice)\n\n  def generateCollections(num: Int, numItems: Int): Seq[CloudStorageCollection] = 1 to num map {\n    i =>\n      CloudStorageCollection(\n        name = collectionName + num,\n        originalSharedCollectionId = Option(originalSharedCollectionId + num),\n        sharedCollectionId = Option(sharedCollectionId + num),\n        sharedCollectionSubscribed = Option(true),\n        generateCollectionItems(num: Int),\n        collectionType = collectionTypeFree,\n        icon = icon + num,\n        category = Option(Business),\n        moment = None)\n  }\n\n  def generateCollectionItems(num: Int): Seq[CloudStorageCollectionItem] = 1 to num map { i =>\n    CloudStorageCollectionItem(\n      itemType = itemType + num,\n      title = itemTitle + num,\n      intent = intentCloud.format(num))\n  }\n\n  def generateMoments(num: Int, numItems: Int): Seq[CloudStorageMoment] = 1 to num map { i =>\n    CloudStorageMoment(\n      timeslot = generateTimeSlots(numItems),\n      wifi = Seq(wifiNetwork + num, nameMobile + num),\n      bluetooth = Option(Seq(bluetoothDevice + num, nameMobile + num)),\n      headphones = headphone,\n      momentType = NineCardsMoment(momentTypeHome),\n      widgets = Some(generateWidgets(numWidgets)))\n  }\n\n  def generateWidgets(num: Int): Seq[CloudStorageWidget] = 1 to num map { i =>\n    CloudStorageWidget(\n      packageName = packageName,\n      className = className,\n      area = generateWidgetArea(i),\n      widgetType = WidgetType(widgetType),\n      label = None,\n      imagePath = None,\n      intent = None)\n  }\n\n  def generateWidgetArea(num: Int): CloudStorageWidgetArea =\n    CloudStorageWidgetArea(startX = num, startY = num, spanX = spanX, spanY = spanY)\n\n  def generateTimeSlots(num: Int): Seq[CloudStorageMomentTimeSlot] = 1 to num map { i =>\n    CloudStorageMomentTimeSlot(from = from, to = to, days = daysSeq)\n  }\n\n  def generateDockApps(num: Int): Seq[CloudStorageDockApp] = 1 to num map { i =>\n    CloudStorageDockApp(\n      name = dockAppName + num,\n      dockType = AppDockType,\n      intent = intentCloud.format(num),\n      imagePath = dockAppImagePath + num,\n      position = num)\n  }\n\n  val cloudStorageDevice =\n    generateCloudStorageDevice(cloudId = cloudId, minusDays = 1, deviceId = deviceId)\n\n  val cloudStorageDeviceSummary = generateCloudStorageDeviceSummary()\n\n  val momentSeq: Option[Seq[MomentData]] = cloudStorageDevice.data.moments map (_ map {\n    case moment =>\n      MomentData(\n        collectionId = None,\n        timeslot = moment.timeslot map { timeSlot =>\n          MomentTimeSlot(from = timeSlot.from, to = timeSlot.to, days = timeSlot.days)\n        },\n        wifi = moment.wifi,\n        bluetooth = moment.bluetooth getOrElse Seq.empty,\n        headphone = moment.headphones,\n        momentType = moment.momentType,\n        widgets = moment.widgets map (_ map { widget =>\n          WidgetData(\n            packageName = widget.packageName,\n            className = widget.className,\n            appWidgetId = None,\n            area = WidgetArea(\n              startX = widget.area.startX,\n              startY = widget.area.startY,\n              spanX = widget.area.spanX,\n              spanY = widget.area.spanY),\n            widgetType = widget.widgetType,\n            label = widget.label,\n            imagePath = widget.imagePath,\n            intent = widget.intent map (intentStr => Json.parse(intentStr).as[NineCardsIntent]))\n        }))\n  })\n\n  val dockAppSeq = cloudStorageDevice.data.dockApps map (_ map {\n    case dockApps =>\n      DockAppData(\n        name = dockApps.name,\n        dockType = dockApps.dockType,\n        intent = Json.parse(dockApps.intent).as[NineCardsIntent],\n        imagePath = dockApps.imagePath,\n        position = dockApps.position)\n  })\n\n  val tokenFirebase = \"token-firebase\"\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/CollectionTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.SharedCollectionValues._\nimport cards.nine.models._\n\ntrait CollectionTestData extends CardTestData with MomentTestData {\n\n  def collection(num: Int = 0) =\n    Collection(\n      id = collectionId + num,\n      position = collectionPosition + num,\n      name = collectionName + num,\n      collectionType = collectionType,\n      icon = icon,\n      themedColorIndex = themedColorIndex,\n      appsCategory = Option(category),\n      cards = Seq(card(0), card(1), card(2)),\n      moment = Option(moment(num)),\n      originalSharedCollectionId = Option(originalSharedCollectionId),\n      sharedCollectionId = Option(sharedCollectionId + num),\n      sharedCollectionSubscribed = sharedCollectionSubscribed,\n      publicCollectionStatus = publicCollectionStatus)\n\n  val collection: Collection         = collection(0)\n  val seqCollection: Seq[Collection] = Seq(collection(0), collection(1), collection(2))\n\n  val collectionData: CollectionData         = collection.toData\n  val seqCollectionData: Seq[CollectionData] = seqCollection map (_.toData)\n\n  val availableMoments =\n    Seq((moment(0), collection(0)), (moment(1), collection(1)), (moment(2), collection(2)))\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/Constants.scala",
    "content": "package cards.nine.commons.test.data\n\nimport android.content.pm.PackageManager\nimport cards.nine.models.MomentTimeSlot\nimport cards.nine.models.types._\n\nobject ApiValues {\n\n  val latitude: Double              = 47d\n  val longitude: Double             = 36d\n  val altitude: Double              = 100d\n  val radius: Int                   = 57\n  val countryCode: String           = \"countryCode\"\n  val countryName: String           = \"countryName\"\n  val street: String                = \"street\"\n  val city: String                  = \"city\"\n  val postalCode: String            = \"postalCode\"\n  val apiIcon: String               = \"apiIcon\"\n  val free: Boolean                 = true\n  val downloads: String             = \"100\"\n  val stars: Double                 = 4.5d\n  val likePackages: Seq[String]     = Seq(\"likePackage1\", \"likePackage2\", \"likePackage3\")\n  val limit: Int                    = 20\n  val apiPackageName: String        = \"apiPackageName\"\n  val apiClassName: String          = \"apiClassName\"\n  val apiTitle: String              = \"apiTitle\"\n  val screenshots: Seq[String]      = Seq(\"screenshot1\", \"screenshot2\", \"screenshot3\")\n  val location: String              = \"EN\"\n  val appId: String                 = \"appId\"\n  val appKey: String                = \"appKey\"\n  val apiPackages: Seq[String]      = Seq(\"apiPackageName0\", \"apiPackageName1\", \"apiPackageName2\")\n  val excludedPackages: Seq[String] = Seq(\"apiPackageName1\")\n\n}\n\nobject ApiV1Values {\n\n  val permissions: Seq[String]    = Seq(\"permission1\", \"permission2\")\n  val itemType: CardType          = AppCardType\n  val title: String               = \"title\"\n  val constrains: Seq[String]     = Seq(\"constrain1\", \"constrain2\")\n  val occurrence: Seq[String]     = Seq(\"occurrence1\", \"occurrence2\")\n  val apiV1CollectionIcon: String = \"GAME\"\n  val deviceIdPrefix: String      = \"deviceIdPrefix\"\n  val userV1Id: String            = \"userV1Id\"\n  val displayName: String         = \"displayName\"\n  val imageUrl: String            = \"imageUrl\"\n  val products: Seq[String]       = Seq(\"product1\", \"product2\")\n\n  val friendsReferred: Int          = 5\n  val themesShared: Int             = 2\n  val collectionsShared: Int        = 10\n  val customCollections: Int        = 4\n  val earlyAdopter: Boolean         = false\n  val communityMember: Boolean      = false\n  val joinedThrough: Option[String] = None\n  val tester: Boolean               = false\n\n}\n\nobject ApplicationValues {\n\n  val keyword                                   = \"keyword\"\n  val applicationId: Int                        = 1\n  val applicationName: String                   = \"applicationName\"\n  val applicationPackageName: String            = \"applicationPackageName\"\n  val nonExistentApplicationPackageName: String = \"nonExistentApplicationPackageName\"\n  val applicationClassName: String              = \"applicationClassName\"\n  val dateInstalled: Long                       = 1l\n  val dateUpdated: Long                         = 2l\n  val version: String                           = \"version\"\n  val installedFromGooglePlay: Boolean          = true\n\n  val deletedApplication: Int  = 1\n  val deletedApplications: Int = 2\n  val updatedApplication: Int  = 1\n  val updatedApplications: Int = 2\n\n}\n\nobject AppWidgetValues {\n\n  val userHashCode: Int            = 1\n  val autoAdvanceViewId: Int       = 1\n  val initialLayout: Int           = 1\n  val minHeight: Int               = 40\n  val minResizeHeight: Int         = 40\n  val minResizeWidth: Int          = 40\n  val minWidth: Int                = 40\n  val appWidgetClassName: String   = \"appWidgetClassName\"\n  val appWidgetPackageName: String = \"appWidgetPackageName\"\n  val resizeMode: Int              = 40\n  val updatePeriodMillis: Int      = 1\n  val label: String                = \"label\"\n  val preview: Int                 = 1\n\n}\n\nobject CardValues {\n\n  val cardId: Int             = 1\n  val nonExistentCardId: Int  = 10001\n  val cardPosition: Int       = 1\n  val term: String            = \"cardTerm\"\n  val cardPackageName: String = \"cardPackageName\"\n  val cardType: String        = \"APP\"\n  val cardImagePath: String   = \"/card/image/path\"\n  val notification: String    = \"notification\"\n  val cardCollectionId: Int   = 1\n\n  val deletedCard: Int  = 1\n  val deletedCards: Int = 2\n  val updatedCard: Int  = 1\n  val updatedCards: Int = 2\n\n  val cardIdReorder: Int       = 1\n  val samePositionReorder: Int = 1\n  val newPositionReorder: Int  = 2\n  val newCardName              = \"newCardName\"\n\n}\n\nobject CloudStorageValues {\n\n  val activeUserId          = 10\n  val cloudId               = \"drive-id\"\n  val anotherCloudId        = \"drive-id-2\"\n  val account               = \"example@domain.com\"\n  val deviceId              = \"device-id\"\n  val anotherDeviceId       = \"device-id-2\"\n  val deviceName            = \"device-name\"\n  val packageName           = \"package-name\"\n  val className             = \"class-name\"\n  val documentVersion       = 1\n  val numCollections        = 1\n  val numItemsPerCollection = 1\n  val numMoments            = 2\n  val numTimeSlot           = 2\n  val numDockApps           = 4\n  val numWidgets            = 2\n  val momentTypeHome        = \"HOME\"\n  val widgetType            = \"APP\"\n  val itemType              = \"item-type\"\n  val itemTitle             = \"item-title\"\n  val wifiNetwork           = \"wifi-network\"\n  val bluetoothDevice       = \"bluetooth-device\"\n  val nameMobile            = \"mobile\"\n  val headphone: Boolean    = false\n  val from: String          = \"8:00\"\n  val to: String            = \"19:00\"\n  val daysSeq: Seq[Int]     = Seq(0, 1, 1, 1, 1, 1, 0)\n  val intentCloud           = \"{ \\\"Item intent\\\":\\\"%s\\\"}\"\n  val spanX                 = 1\n  val spanY                 = 1\n\n}\n\nobject CollectionValues {\n\n  val collectionId: Int                              = 1\n  val nonExistentCollectionId: Int                   = 10001\n  val collectionPosition: Int                        = 1\n  val collectionNewPosition: Int                     = 5\n  val nonExistentCollectionPosition: Int             = 10001\n  val collectionName: String                         = \"collectionName\"\n  val collectionType: CollectionType                 = AppsCollectionType\n  val collectionTypeFree: CollectionType             = FreeCollectionType\n  val icon: String                                   = \"icon\"\n  val themedColorIndex: Int                          = 1\n  val originalSharedCollectionId: String             = \"originalSharedCollection\"\n  val sharedCollectionSubscribed: Boolean            = false\n  val publicCollectionStatus: PublicCollectionStatus = PublishedByOther\n  val installations: Int                             = 20\n\n  val deletedCollection: Int                = 1\n  val deletedCollections: Int               = 2\n  val updatedCollection: Int                = 1\n  val updatedCollections: Int               = 2\n  val newCollectionName: String             = \"newCollectionName\"\n  val newCollectionIcon: String             = \"newCollectionIcon\"\n  val newThemedColorIndex: Int              = 1\n  val newSharedCollectionId: String         = \"newSharedCollectionId\"\n  val nonExistentSharedCollectionId: String = \"nonExistentSharedCollectionId\"\n\n  val scrollY             = 1\n  val backgroundColor     = 1\n  val initialToolbarColor = 1\n  val stateChanged        = true\n\n}\n\nobject CommonValues {\n\n  val categoryStr: String                = \"SOCIAL\"\n  val category: NineCardsCategory        = NineCardsCategory(categoryStr)\n  val anotherCategory: NineCardsCategory = Communication\n  val intent: String =\n    \"\"\"{\"className\":\"classNameValue\",\"packageName\":\"packageNameValue\",\"categories\":[\"category1\"],\"action\":\"actionValue\",\"extras\":{\"pairValue\":\"pairValue\",\"empty\":false,\"parcelled\":false},\"flags\":1,\"type\":\"typeValue\"}\"\"\"\n  val wifiSeq: Seq[String] =\n    Seq(\"wifi 1\", \"wifi 2\", \"wifi 3\", \"wifi 4\", \"wifi 5\", \"wifi 6\", \"wifi 7\", \"wifi 8\")\n  val bluetoothSeq: Seq[String] =\n    Seq(\n      \"bluetooth 1\",\n      \"bluetooth 2\",\n      \"bluetooth 3\",\n      \"bluetooth 4\",\n      \"bluetooth 5\",\n      \"bluetooth 6\",\n      \"bluetooth 7\",\n      \"bluetooth 8\")\n  val sharedCollectionId: String = \"sharedCollectionId\"\n}\n\nobject DeviceValues {\n\n  val shortcutName: String         = \"shortcutName\"\n  val contactName: String          = \"contactName\"\n  val lookupKey: String            = \"lookupKey\"\n  val photoUri: String             = \"photoUri\"\n  val hasPhone: Boolean            = false\n  val favorite: Boolean            = false\n  val emailAddress: String         = \"contact@email.com\"\n  val emailCategory: EmailCategory = EmailHome\n  val phoneNumber: String          = \"contact@email.com\"\n  val phoneCategory: PhoneCategory = PhoneHome\n  val date: Long                   = 3l\n  val callType: CallType           = IncomingType\n  val hasContact: Boolean          = true\n  val contactKeyword               = \"contactKeyword\"\n  val appKeyword                   = \"appKeyword\"\n  val fileNameShortcut             = s\"/path/shortcut/$shortcutName\"\n  val contactPermissions           = Array(android.Manifest.permission.READ_CONTACTS)\n  val contactNoPermissions         = Array(android.Manifest.permission.CALL_PHONE)\n  val contactGranResults           = Array(PackageManager.PERMISSION_GRANTED)\n\n}\n\nobject DockAppValues {\n\n  val dockAppSize: Int          = 3\n  val dockAppId: Int            = 1\n  val nonExistentDockAppId: Int = 10001\n  val dockAppName: String       = \"dockAppName\"\n  val dockType: String          = \"APP\"\n  val dockAppImagePath: String  = \"/dockApp/image/path\"\n  val dockAppPosition: Int      = 1\n\n  val deletedDockApp: Int  = 1\n  val deletedDockApps: Int = 5\n\n}\n\nobject LauncherExecutorValues {\n\n  val exceptionMessage: String = \"exceptionMessage\"\n\n  val launcherExecutorPackageName: String = \"launcherExecutorPackageName\"\n  val launcherExecutorClassName: String   = \"launcherExecutorClassName\"\n  val googlePlayUrl: String               = \"http://googlePlayUrl\"\n  val launcherExecutorUrl: String         = \"http://launcherExecutorUrl\"\n  val launcherExecutorLookupKey: String   = \"launcherExecutorLookupKey\"\n  val launcherExecutorEmail: String       = \"launcherExecutor@email.com\"\n  val launcherExecutorPhoneNumber: String = \"666 66 66 66\"\n  val shareText: String                   = \"Share text\"\n  val emailTitleDialog: String            = \"Email Title Dialog\"\n  val shareTitleDialog: String            = \"Share Title Dialog\"\n  val unknownAction: String               = \"Unknown action\"\n}\n\nobject MomentValues {\n\n  val momentId: Int            = 1\n  val nonExistentMomentId: Int = 10001\n  val momentCollectionId: Int  = 1\n  val timeslotJson: String =\n    \"\"\"[{\"from\":\"from1\",\"to\":\"to1\",\"days\":[11,12,13]},{\"from\":\"from2\",\"to\":\"to2\",\"days\":[21,22,23]}]\"\"\"\n  val headphone: Boolean              = false\n  val momentSeq: Seq[NineCardsMoment] = NineCardsMoment.moments\n  val momentTypeSeq: Seq[String]      = momentSeq.map(_.name)\n  val momentType: NineCardsMoment     = HomeMorningMoment\n  val homeAppPackageName              = \"com.google.android.apps.plus\"\n  val nightAppPackageName             = \"com.Slack\"\n  val workAppPackageName              = \"com.google.android.apps.photos\"\n  val transitAppPackageName           = \"com.google.android.apps.maps\"\n\n  val deletedMoment: Int  = 1\n  val deletedMoments: Int = 2\n  val updatedMoment: Int  = 1\n\n  val position: Int        = 1\n  val noFoundPosition: Int = 20\n  val day: Int             = 1\n  val hour: String         = \"8:00\"\n  val newTimeslot          = MomentTimeSlot(from = \"9:00\", to = \"14:00\", days = Seq(0, 0, 0, 0, 0, 0, 0))\n\n}\n\nobject SharedCollectionValues {\n\n  val sharedCollectionPackageName: String  = \"sharedCollectionPackageName\"\n  val sharedCollectionPackageTitle: String = \"sharedCollectionPackageTitle\"\n  val sharedCollectionPackageIcon: String  = \"sharedCollectionPackageIcon\"\n  val sharedCollectionPackageCategory      = Some(Social)\n  val sharedCollectionPackageStars: Double = 4.2d\n  val sharedCollectionDownloads: String    = \"28\"\n  val sharedCollectionFree: Boolean        = true\n\n  val publishedOn: Long            = 1471359330574l\n  val publishedOnStr: String       = \"2016-08-16T14:55:30.574000\"\n  val author: String               = \"author\"\n  val owned: Boolean               = false\n  val sharedCollectionName: String = \"sharedCollectionName\"\n  val sharedCollectionPackageNamesStr: Seq[String] = Seq(\n    \"sharedCollectionPackageName0\",\n    \"sharedCollectionPackageName1\",\n    \"sharedCollectionPackageName2\")\n  val views: Int                   = 29\n  val subscriptions: Int           = 6\n  val sharedCollectionIcon: String = \"sharedCollectionIcon\"\n  val community: Boolean           = false\n\n  val typeShareCollection: TypeSharedCollection = TopSharedCollection\n  val offset: Int                               = 0\n\n}\n\nobject TrackEventValues {\n\n  val newPosition: Int            = 8\n  val collectionName: String      = \"collectionName\"\n  val packageNameSeq: Seq[String] = Seq(\"packageName0\", \"packageName1\", \"packageName2\")\n  val packageNameSeqStr: String   = \"packageName0,packageName1,packageName2\"\n  val shortcutName: String        = \"shortcutName\"\n  val packageName: String         = \"packageName\"\n  val filterName: String          = \"filterName\"\n  val momentName: String          = \"momentName\"\n  val widgetName: String          = \"widgetName\"\n  val position: Int               = 1\n  val supported: Boolean          = true\n  val supportedStr: String        = \"Supported\"\n  val notSupported: Boolean       = false\n  val notSupportedStr: String     = \"Not Supported\"\n\n  val entertainmentPackageName: String   = \"package.name.entertainment\"\n  val entertainmentCategory: AppCategory = AppCategory(Entertainment)\n\n  val gamePackageName: String   = \"package.name.game\"\n  val gameCategory: AppCategory = AppCategory(GameAdventure)\n\n  val momentPackageName: String      = \"package.name.moment\"\n  val momentCategory: MomentCategory = MomentCategory(HomeMorningMoment)\n  val momentClassName: String        = \"class.name.moment\"\n\n  val publication: String        = \"publication\"\n  val sharedCollectionId: String = \"sharedCollectionId\"\n}\n\nobject UserValues {\n\n  val userId: Int                   = 1\n  val newUserId: Int                = 10\n  val nonExistentUserId: Int        = 10001\n  val email: String                 = \"user@email.com\"\n  val sessionToken: String          = \"sessionToken\"\n  val apiKey: String                = \"apiKey\"\n  val deviceToken: String           = \"deviceToken\"\n  val anotherDeviceToken            = \"anotherDeviceToken\"\n  val marketToken: String           = \"marketToken\"\n  val userDeviceName: String        = \"deviceName\"\n  val userDeviceNameDefault: String = \"deviceName0\"\n  val anotherUserDeviceName         = \"anotherDeviceName\"\n  val userDeviceId                  = \"deviceId\"\n  val userDeviceIdDefault           = \"deviceId0\"\n  val deviceCloudId: String         = \"deviceCloudId\"\n  val anotherDeviceCloudId          = \"anotherDeviceCloudId\"\n  val userName: String              = \"userName\"\n  val avatar: String                = \"avatar\"\n  val cover: String                 = \"cover\"\n  val androidId                     = \"androidId\"\n  val emailTokenId                  = \"emailTokenId\"\n  val tokenId                       = \"tokenId\"\n\n  val deletedUser: Int  = 1\n  val deletedUsers: Int = 2\n  val updatedUser: Int  = 1\n  val updatedUsers: Int = 2\n\n}\n\nobject UserV1Values {\n\n  val baseUrl    = \"http://baseUrl\"\n  val statusCode = 200\n\n  val userV1Name: String                 = \"userV1Name\"\n  val userV1Password: String             = \"userV1Password\"\n  val authFacebookId: String             = \"authFacebookId\"\n  val authFacebookAccessToken: String    = \"authFacebookAccessToken\"\n  val authFacebookExpirationDate: Long   = 10l\n  val authTwitterId: String              = \"authTwitterId\"\n  val authTwitterScreenName: String      = \"authTwitterScreenName\"\n  val authTwitterConsumerKey: String     = \"authTwitterConsumerKey\"\n  val authTwitterConsumerSecret: String  = \"authTwitterConsumerSecret\"\n  val authTwitterAuthToken: String       = \"authTwitterAuthToken\"\n  val authTwitterAuthTokenSecret: String = \"authTwitterAuthTokenSecret\"\n  val authTwitterKey: String             = \"authTwitterKey\"\n  val authTwitterSecretKey: String       = \"authTwitterSecretKey\"\n  val authAnonymousId: String            = \"authAnonymousId\"\n\n  val userConfigPlusImageType: Int    = 0\n  val userConfigPlusSecureUrl: String = \"userConfigPlusSecureUrl\"\n  val screenshot: String              = \"screenshot\"\n\n  val collectionTypeTop: String     = \"top\"\n  val collectionTypeLatest: String  = \"latest\"\n  val collectionTypeUnknown: String = \"unknown\"\n\n  val userV1Radius: Int            = 57\n  val userV1Latitude: Double       = 57d\n  val userV1Longitude: Double      = 45d\n  val userV1Altitude: Double       = 100d\n  val userV1PublishedOnStr: String = \"2016-08-16T14:55:30.574000\"\n\n  val searchString = \"seach string\"\n\n}\n\nobject WidgetValues {\n\n  val widgetId: Int                  = 1\n  val nonExistentWidgetId: Int       = 10001\n  val widgetMomentId: Int            = 1\n  val nonExistentWidgetMomentId: Int = 1\n  val widgetPackageName: String      = \"widgetPackageName\"\n  val widgetClassName: String        = \"widgetClassName\"\n  val appWidgetId: Int               = 1\n  val nonExistentAppWidgetId: Int    = 10001\n  val startX: Int                    = 0\n  val startY: Int                    = 0\n  val spanX: Int                     = 1\n  val spanY: Int                     = 1\n  val widgetType: String             = \"APP\"\n  val label: String                  = \"widget label\"\n  val widgetImagePath: String        = \"/widget/image/path\"\n\n  val displaceX: Int = 2\n  val displaceY: Int = 2\n  val increaseX: Int = 1\n  val increaseY: Int = 1\n\n  val deletedWidget: Int  = 1\n  val deletedWidgets: Int = 2\n  val updatedWidget: Int  = 1\n  val updatedWidgets: Int = 2\n\n}\n\nobject WizardJobsValues {\n\n  val email            = \"test@test.com\"\n  val token            = \"authToken\"\n  val keyDevice        = \"key-device\"\n  val emailTokenId     = \"email-tokenId\"\n  val requestCodeError = 0\n  val requestCode      = 17\n  val permissions      = Array(android.Manifest.permission.ACCESS_FINE_LOCATION)\n  val granResults      = Array(1)\n  val profileName      = \"profileName\"\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/DeviceTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.DeviceValues._\nimport cards.nine.models._\nimport cards.nine.models.types.AudioAndVideoBluetooth\n\ntrait DeviceTestData extends NineCardsIntentConversions {\n\n  def shortcut(num: Int = 0) =\n    Shortcut(title = shortcutName + num, icon = None, intent = jsonToNineCardIntent(intent))\n\n  val shortcut: Shortcut         = shortcut(0)\n  val seqShortcut: Seq[Shortcut] = Seq(shortcut(0), shortcut(1), shortcut(2))\n\n  def contactEmail(num: Int = 0) =\n    ContactEmail(address = emailAddress + num, category = emailCategory)\n\n  val seqContactEmail: Seq[ContactEmail] = Seq(contactEmail(0), contactEmail(1), contactEmail(2))\n\n  def contactPhone(num: Int = 0) =\n    ContactPhone(number = phoneNumber + num, category = phoneCategory)\n\n  val seqContactPhone: Seq[ContactPhone] = Seq(contactPhone(0), contactPhone(1), contactPhone(2))\n\n  def contactInfo(num: Int = 0) = ContactInfo(emails = seqContactEmail, phones = seqContactPhone)\n\n  val contactInfo: ContactInfo = contactInfo(0)\n\n  def contact(num: Int = 0) =\n    Contact(\n      name = contactName + num,\n      lookupKey = lookupKey + num,\n      photoUri = photoUri + num,\n      hasPhone = hasPhone,\n      favorite = favorite,\n      info = Option(contactInfo))\n\n  val contact: Contact         = contact(0)\n  val seqContact: Seq[Contact] = Seq(contact(0), contact(1), contact(2))\n\n  def call(num: Int = 0) =\n    Call(\n      number = phoneNumber + num,\n      name = Option(contactName + num),\n      numberType = phoneCategory,\n      date = date + num,\n      callType = callType)\n\n  val call: Call         = call(0)\n  val seqCall: Seq[Call] = Seq(call(0), call(1), call(2))\n\n  def lastCallsContact(num: Int = 0) =\n    LastCallsContact(\n      hasContact = hasContact,\n      number = phoneNumber + num,\n      title = contactName + num,\n      photoUri = Option(photoUri + num),\n      lookupKey = Option(lookupKey + num),\n      lastCallDate = date + num,\n      calls = Seq(call(num)))\n\n  val lastCallsContact: LastCallsContact = lastCallsContact(0)\n  val seqLastCallsContact: Seq[LastCallsContact] =\n    Seq(lastCallsContact(0), lastCallsContact(1), lastCallsContact(2))\n  val seqLastCallsContactByDate: Seq[LastCallsContact] =\n    seqLastCallsContact.sortBy(_.lastCallDate).reverse\n\n  val appsCounters = Seq(\n    TermCounter(\"#\", 4),\n    TermCounter(\"B\", 1),\n    TermCounter(\"E\", 6),\n    TermCounter(\"F\", 5),\n    TermCounter(\"Z\", 3))\n\n  val categoryCounters = Seq(\n    TermCounter(\"COMMUNICATION\", 4),\n    TermCounter(\"GAMES\", 1),\n    TermCounter(\"SOCIAL\", 6),\n    TermCounter(\"TOOLS\", 5),\n    TermCounter(\"WEATHER\", 3))\n\n  val contactsCounters = Seq(\n    TermCounter(\"#\", 4),\n    TermCounter(\"B\", 1),\n    TermCounter(\"E\", 6),\n    TermCounter(\"F\", 5),\n    TermCounter(\"Z\", 3))\n\n  val installationAppsCounters = Seq(\n    TermCounter(\"oneWeek\", 4),\n    TermCounter(\"twoWeeks\", 1),\n    TermCounter(\"oneMonth\", 6),\n    TermCounter(\"twoMonths\", 5))\n\n  val networks = 0 to 10 map (c => s\"Networks $c\")\n\n  val bluetoothDevices = 0 to 10 map { c =>\n    NineCardsBluetoothDevice(\n      name = s\"Bluetooth $c\",\n      address = s\"Address $c\",\n      bluetoothType = AudioAndVideoBluetooth\n    )\n  }\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/DockAppTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.DockAppValues._\nimport cards.nine.models.types.DockType\nimport cards.nine.models.{DockApp, DockAppData, NineCardsIntentConversions}\n\ntrait DockAppTestData extends NineCardsIntentConversions {\n\n  def dockApp(num: Int = 0) =\n    DockApp(\n      id = dockAppId + num,\n      name = dockAppName,\n      dockType = DockType(dockType),\n      intent = jsonToNineCardIntent(intent),\n      imagePath = dockAppImagePath,\n      position = dockAppPosition)\n\n  val dockApp: DockApp         = dockApp(0)\n  val seqDockApp: Seq[DockApp] = Seq(dockApp(0), dockApp(1), dockApp(2))\n\n  val dockAppData: DockAppData         = dockApp.toData\n  val seqDockAppData: Seq[DockAppData] = seqDockApp map (_.toData)\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/LauncherExecutorTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.models._\nimport cards.nine.commons.test.data.LauncherExecutorValues._\n\ntrait LauncherExecutorTestData {\n\n  val appAction             = AppAction(launcherExecutorPackageName, launcherExecutorClassName)\n  val appGooglePlayAction   = AppGooglePlayAction(googlePlayUrl, launcherExecutorPackageName)\n  val appLauncherAction     = AppLauncherAction(launcherExecutorPackageName)\n  val appSettingsAction     = AppSettingsAction(launcherExecutorPackageName)\n  val appUninstallAction    = AppUninstallAction(launcherExecutorPackageName)\n  val contactAction         = ContactAction(launcherExecutorLookupKey)\n  val emailAction           = EmailAction(launcherExecutorEmail, emailTitleDialog)\n  val globalSettingsAction  = GlobalSettingsAction\n  val googlePlayStoreAction = GooglePlayStoreAction\n  val googleWeatherAction   = GoogleWeatherAction\n  val phoneSmsAction        = PhoneSmsAction(launcherExecutorPhoneNumber)\n  val phoneCallAction       = PhoneCallAction(launcherExecutorPhoneNumber)\n  val phoneDialAction       = PhoneDialAction(Some(launcherExecutorPhoneNumber))\n  val searchGlobalAction    = SearchGlobalAction\n  val searchVoiceAction     = SearchVoiceAction\n  val searchWebAction       = SearchWebAction\n  val shareAction           = ShareAction(shareText, shareTitleDialog)\n  val urlAction             = UrlAction(launcherExecutorUrl)\n\n  val config = LauncherExecutorProcessConfig(googlePlayUrl, emailTitleDialog, shareTitleDialog)\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/MomentTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.MomentValues._\nimport cards.nine.models.reads.MomentImplicits\nimport cards.nine.models.types._\nimport cards.nine.models.{Moment, MomentData, MomentTimeSlot}\nimport play.api.libs.json.Json\nimport cards.nine.models.Moment.MomentTimeSlotOps\n\ntrait MomentTestData extends WidgetTestData {\n\n  import MomentImplicits._\n\n  def moment(num: Int = 0) =\n    Moment(\n      id = momentId + num,\n      collectionId = Option(momentCollectionId + num),\n      timeslot = Json.parse(timeslotJson).as[Seq[MomentTimeSlot]],\n      wifi = Seq(wifiSeq(num)),\n      bluetooth = Seq(bluetoothSeq(num)),\n      headphone = headphone,\n      momentType = NineCardsMoment(momentTypeSeq(num)),\n      widgets = Option(seqWidgetData))\n\n  val moment: Moment         = moment(0)\n  val seqMoment: Seq[Moment] = Seq(moment(0), moment(1), moment(2))\n\n  val momentData: MomentData         = moment.toData\n  val seqMomentData: Seq[MomentData] = seqMoment map (_.toData)\n\n  def momentData(infoMoment: (NineCardsMoment, Option[String])) =\n    MomentData(\n      collectionId = None,\n      timeslot = infoMoment._1.toMomentTimeSlot,\n      wifi = infoMoment._2.toSeq,\n      bluetooth = Seq.empty,\n      headphone = false,\n      momentType = infoMoment._1)\n\n  val minMomentsWithWifi = Seq(momentData(NineCardsMoment.defaultMoment, None))\n  val homeNightMoment    = Seq(momentData(HomeNightMoment, Option(\"wifi\")))\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/SharedCollectionTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.commons.test.data.SharedCollectionValues._\nimport cards.nine.models.types.AppCardType\nimport cards.nine.models.{SharedCollection, SharedCollectionPackage, Subscription}\n\ntrait SharedCollectionTestData extends CollectionTestData {\n\n  def sharedCollectionPackage(num: Int = 0) =\n    SharedCollectionPackage(\n      packageName = sharedCollectionPackageName + num,\n      title = sharedCollectionPackageTitle + num,\n      icon = sharedCollectionPackageIcon,\n      category = sharedCollectionPackageCategory,\n      stars = sharedCollectionPackageStars,\n      downloads = sharedCollectionDownloads,\n      free = sharedCollectionFree)\n\n  val sharedCollectionPackage: SharedCollectionPackage = sharedCollectionPackage(0)\n  val seqSharedCollectionPackage: Seq[SharedCollectionPackage] =\n    Seq(sharedCollectionPackage(0), sharedCollectionPackage(1), sharedCollectionPackage(2))\n\n  def sharedCollection(num: Int = 0) =\n    SharedCollection(\n      id = sharedCollectionId + num,\n      sharedCollectionId = sharedCollectionId + num,\n      publishedOn = publishedOn,\n      author = author,\n      name = sharedCollectionName,\n      packages = sharedCollectionPackageNamesStr,\n      resolvedPackages = seqSharedCollectionPackage,\n      views = views,\n      subscriptions = Some(subscriptions),\n      category = category,\n      icon = sharedCollectionIcon,\n      community = community,\n      locallyAdded = None,\n      publicCollectionStatus = publicCollectionStatus)\n\n  val sharedCollection: SharedCollection = sharedCollection(0)\n  val seqSharedCollection: Seq[SharedCollection] =\n    Seq(sharedCollection(0), sharedCollection(1), sharedCollection(2))\n\n  val publicationListIds = seqSharedCollection.map(_.sharedCollectionId)\n\n  val seqPublicCollection =\n    seqCollection\n      .flatMap(collection => collection.originalSharedCollectionId.map((_, collection)))\n      .filter {\n        case (sharedCollectionId: String, _) => !publicationListIds.contains(sharedCollectionId)\n      }\n\n  def subscription(num: Int = 0) =\n    Subscription(\n      id = collection.id + num,\n      sharedCollectionId = sharedCollectionId + num,\n      name = collection.name,\n      apps = collection.cards.count(card => card.cardType == AppCardType),\n      icon = collection.icon,\n      themedColorIndex = collection.themedColorIndex,\n      subscribed = collection.sharedCollectionSubscribed)\n\n  val subscription: Subscription          = subscription(0)\n  val seqSubscriptions: Seq[Subscription] = Seq(subscription(0), subscription(1), subscription(2))\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/UserTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.models.{User, UserData, UserProfile}\n\ntrait UserTestData {\n\n  def user(num: Int = 0) =\n    User(\n      id = userId + num,\n      email = Option(email),\n      sessionToken = Option(sessionToken),\n      apiKey = Option(apiKey),\n      deviceToken = Option(deviceToken),\n      marketToken = Option(marketToken),\n      deviceName = Option(userDeviceName),\n      deviceCloudId = Option(deviceCloudId),\n      userProfile =\n        UserProfile(name = Option(userName), avatar = Option(avatar), cover = Option(cover)))\n\n  val user: User         = user(0)\n  val anotherUser: User  = user(1)\n  val seqUser: Seq[User] = Seq(user(0), user(1), user(2))\n\n  val userData: UserData         = user.toData\n  val seqUserData: Seq[UserData] = seqUser map (_.toData)\n\n  val emptyUser =\n    User(userId, None, None, None, None, None, None, None, UserProfile(None, None, None))\n  val emptyUserData =\n    UserData(None, None, None, None, None, None, None, UserProfile(None, None, None))\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/WidgetTestData.scala",
    "content": "package cards.nine.commons.test.data\n\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.WidgetValues._\nimport cards.nine.models.types.WidgetType\nimport cards.nine.models.{NineCardsIntentConversions, Widget, WidgetArea, WidgetData}\n\ntrait WidgetTestData extends NineCardsIntentConversions {\n\n  def widget(num: Int = 0) =\n    Widget(\n      id = widgetId + num,\n      momentId = widgetMomentId,\n      packageName = widgetPackageName,\n      className = widgetClassName,\n      appWidgetId = Option(appWidgetId),\n      area = WidgetArea(startX = startX, startY = startY, spanX = spanX, spanY = spanY),\n      widgetType = WidgetType(widgetType),\n      label = Option(label),\n      imagePath = Option(widgetImagePath),\n      intent = Option(jsonToNineCardIntent(intent)))\n\n  val widget: Widget = widget(0)\n  val seqWidget      = Seq(widget(0), widget(1), widget(2))\n\n  val widgetData: WidgetData = widget.toData\n  val seqWidgetData          = seqWidget map (_.toData)\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/trackevent/AppDrawerTrackEventTestData.scala",
    "content": "package cards.nine.commons.test.data.trackevent\n\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\n\ntrait AppDrawerTrackEventTestData {\n\n  val usingFastScrollerEvent = TrackEvent(\n    screen = AppDrawerScreen,\n    category = FastScrollerCategory,\n    action = UsingFastScrollerAction,\n    label = None,\n    value = None)\n\n  val goToContactsEvent = TrackEvent(\n    screen = AppDrawerScreen,\n    category = GestureActionsCategory,\n    action = GoToContactsAction,\n    label = None,\n    value = None)\n\n  val goToAppsEvent = TrackEvent(\n    screen = AppDrawerScreen,\n    category = GestureActionsCategory,\n    action = GoToAppsAction,\n    label = None,\n    value = None)\n\n  val addAppToCollectionEvent = TrackEvent(\n    screen = AppDrawerScreen,\n    category = GestureActionsCategory,\n    action = AddAppToCollectionAction,\n    label = Option(packageName),\n    value = None)\n\n  val addContactToCollectionEvent = TrackEvent(\n    screen = AppDrawerScreen,\n    category = GestureActionsCategory,\n    action = AddContactToCollectionAction,\n    label = None,\n    value = None)\n\n  val goToGooglePlayButtonEvent = TrackEvent(\n    screen = AppDrawerScreen,\n    category = SearchButtonsCategory,\n    action = GoToGooglePlayButtonAction,\n    label = None,\n    value = None)\n\n  val goToGoogleCallButtonEvent = TrackEvent(\n    screen = AppDrawerScreen,\n    category = SearchButtonsCategory,\n    action = GoToGoogleCallButtonAction,\n    label = None,\n    value = None)\n\n  val goToFiltersByButtonEvent = TrackEvent(\n    screen = AppDrawerScreen,\n    category = SearchButtonsCategory,\n    action = GoToFiltersByButtonAction,\n    label = Option(filterName),\n    value = None)\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/trackevent/CollectionDetailTrackEventTestData.scala",
    "content": "package cards.nine.commons.test.data.trackevent\n\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\n\ntrait CollectionDetailTrackEventTestData {\n\n  val useNavigationBarEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = NavigationBarAction,\n    label = None,\n    value = None)\n\n  val reorderApplicationEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = ReorderApplicationAction,\n    label = Option(newPosition.toString),\n    value = None)\n\n  val moveApplicationsEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = MoveApplicationsAction,\n    label = Option(collectionName),\n    value = None)\n\n  val removeApplicationsEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = RemoveApplicationsAction,\n    label = Option(packageNameSeqStr),\n    value = None)\n\n  val closeCollectionByGestureEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = CloseCollectionByGestureAction,\n    label = None,\n    value = None)\n\n  val addShortcutByFabEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = AddShortcutByFabAction,\n    label = Option(shortcutName),\n    value = None)\n\n  val addRecommendationByFabEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = AddRecommendationByFabAction,\n    label = Option(packageName),\n    value = None)\n\n  val addContactByFabEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = AddContactByFabAction,\n    label = None,\n    value = None)\n\n  val addAppsByFabEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = AddAppsByFabAction,\n    label = Option(packageNameSeqStr),\n    value = None)\n\n  val removeAppsByFabEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = RemoveAppsByFabAction,\n    label = Option(packageNameSeqStr),\n    value = None)\n\n  val addCardByMenuEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = AddCardByMenuAction,\n    label = None,\n    value = None)\n\n  val publishCollectionByMenuEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = PublishCollectionByMenuAction,\n    label = Option(collectionName),\n    value = None)\n\n  val shareCollectionAfterPublishingEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = ShareCollectionAfterPublishingAction,\n    label = Option(sharedCollectionId),\n    value = None)\n\n  val shareCollectionByMenuEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = GestureActionsCategory,\n    action = ShareCollectionByMenuAction,\n    label = Option(sharedCollectionId),\n    value = None)\n\n  val openAppFromCollectionEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = entertainmentCategory,\n    action = OpenCardAction,\n    label = Option(entertainmentPackageName),\n    value = Option(OpenAppFromCollectionValue))\n\n  val addAppEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = entertainmentCategory,\n    action = AddedToCollectionAction,\n    label = Option(entertainmentPackageName),\n    value = Option(AddedToCollectionValue))\n\n  val removeEvent = TrackEvent(\n    screen = CollectionDetailScreen,\n    category = entertainmentCategory,\n    action = RemovedFromCollectionAction,\n    label = Option(entertainmentPackageName),\n    value = Option(RemovedFromCollectionValue))\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/trackevent/HomeTrackEventTestData.scala",
    "content": "package cards.nine.commons.test.data.trackevent\n\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\n\ntrait HomeTrackEventTestData {\n\n  val openCollectionTitleEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceCategory,\n    action = OpenCollectionTitleAction,\n    label = Option(collectionName),\n    value = None)\n\n  val openCollectionOrderEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceCategory,\n    action = OpenCollectionOrderAction,\n    label = Option(position.toString),\n    value = None)\n\n  val deleteCollectionEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceDragAndDropCategory,\n    action = DeleteCollectionAction,\n    label = Option(collectionName),\n    value = None)\n\n  val reorderCollectionEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceCategory,\n    action = ReorderCollectionAction,\n    label = None,\n    value = None)\n\n  val usingSearchByKeyboardEvent = TrackEvent(\n    screen = HomeScreen,\n    category = SearchButtonsCategory,\n    action = UsingSearchByKeyboardAction,\n    label = None,\n    value = None)\n\n  val usingSearchByVoiceEvent = TrackEvent(\n    screen = HomeScreen,\n    category = SearchButtonsCategory,\n    action = UsingSearchByVoiceAction,\n    label = None,\n    value = None)\n\n  val createNewCollectionEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceActionsCategory,\n    action = CreateNewCollectionAction,\n    label = None,\n    value = None)\n\n  val editCollectionEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceActionsCategory,\n    action = EditCollectionAction,\n    label = Option(collectionName),\n    value = None)\n\n  val openMyCollectionsEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceActionsCategory,\n    action = OpenMyCollectionsAction,\n    label = None,\n    value = None)\n\n  val openPublicCollectionsEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceActionsCategory,\n    action = OpenPublicCollectionsAction,\n    label = None,\n    value = None)\n\n  val createNewCollectionFromMyCollectionEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceActionsCategory,\n    action = CreateNewCollectionFromMyCollectionAction,\n    label = Option(collectionName),\n    value = None)\n\n  val createNewCollectionFromPublicCollectionEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceActionsCategory,\n    action = CreateNewCollectionFromPublicCollectionAction,\n    label = Option(collectionName),\n    value = None)\n\n  val openDockAppTitleEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceBottomActionsCategory,\n    action = OpenDockAppTitleAction,\n    label = Option(packageName),\n    value = None)\n\n  val openDockAppOrderEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceBottomActionsCategory,\n    action = OpenDockAppOrderAction,\n    label = Option(position.toString),\n    value = None)\n\n  val goToAppDrawerEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceBottomActionsCategory,\n    action = GoToAppDrawerAction,\n    label = None,\n    value = None)\n\n  val appLinkReceivedEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceLinkReceived,\n    action = AppLinkReceivedAction,\n    label = Option(supportedStr),\n    value = None)\n\n  val sharedContentReceivedEvent = TrackEvent(\n    screen = HomeScreen,\n    category = WorkSpaceLinkReceived,\n    action = SharedContentReceivedAction,\n    label = Option(supportedStr),\n    value = None)\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/trackevent/LauncherTrackEventTestData.scala",
    "content": "package cards.nine.commons.test.data.trackevent\n\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types.{LauncherScreen, OpenAction, OpenAppFromAppDrawerValue}\n\ntrait LauncherTrackEventTestData {\n\n  val openAppEntertainmentEvent = TrackEvent(\n    screen = LauncherScreen,\n    category = entertainmentCategory,\n    action = OpenAction,\n    label = Option(entertainmentPackageName),\n    value = Option(OpenAppFromAppDrawerValue))\n\n  val openAppGameEvent = TrackEvent(\n    screen = LauncherScreen,\n    category = gameCategory,\n    action = OpenAction,\n    label = Option(gamePackageName),\n    value = Option(OpenAppFromAppDrawerValue))\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/trackevent/MomentsTrackEventTestData.scala",
    "content": "package cards.nine.commons.test.data.trackevent\n\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\n\ntrait MomentsTrackEventTestData {\n\n  val openApplicationByMomentEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = IconBarCategory,\n    action = OpenApplicationByMomentAction,\n    label = Option(momentName),\n    value = None)\n\n  val editMomentEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = WorkSpaceActionsCategory,\n    action = EditMomentAction,\n    label = Option(momentName),\n    value = None)\n\n  val changeMomentEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = WorkSpaceActionsCategory,\n    action = ChangeMomentAction,\n    label = Option(momentName),\n    value = None)\n\n  val addMomentEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = WorkSpaceActionsCategory,\n    action = AddMomentAction,\n    label = Option(momentName),\n    value = None)\n\n  val addWidgetEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = WorkSpaceActionsCategory,\n    action = AddWidgetAction,\n    label = Option(widgetName),\n    value = None)\n\n  val unpinMomentEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = TopBarCategory,\n    action = UnpinMomentAction,\n    label = None,\n    value = None)\n\n  val goToWeatherEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = TopBarCategory,\n    action = GoToWeatherAction,\n    label = None,\n    value = None)\n\n  val quickAccessToCollectionEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = EditMomentCategory,\n    action = QuickAccessToCollectionAction,\n    label = None,\n    value = None)\n\n  val setHoursEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = EditMomentCategory,\n    action = SetHoursAction,\n    label = None,\n    value = None)\n\n  val setWifiEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = EditMomentCategory,\n    action = SetWifiAction,\n    label = None,\n    value = None)\n\n  val setBluetoothEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = EditMomentCategory,\n    action = SetBluetoothAction,\n    label = None,\n    value = None)\n\n  val chooseMomentEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = MomentsMenuCategory,\n    action = ChooseMomentAction,\n    label = Option(momentName),\n    value = None)\n\n  val editMomentByMenuEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = MomentsMenuCategory,\n    action = EditMomentAction,\n    label = Option(momentName),\n    value = None)\n\n  val deleteMomentEvent = TrackEvent(\n    screen = MomentsScreen,\n    category = MomentsMenuCategory,\n    action = DeleteMomentAction,\n    label = None,\n    value = None)\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/trackevent/ProfileTrackEventTestData.scala",
    "content": "package cards.nine.commons.test.data.trackevent\n\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\n\ntrait ProfileTrackEventTestData {\n\n  val logoutEvent = TrackEvent(\n    screen = ProfileScreen,\n    category = AccountCategory,\n    action = LogoutAction,\n    label = None,\n    value = None)\n\n  val showAccountsContentEvent = TrackEvent(\n    screen = ProfileScreen,\n    category = AccountCategory,\n    action = ShowAccountsContentAction,\n    label = None,\n    value = None)\n\n  val copyConfigurationEvent = TrackEvent(\n    screen = ProfileScreen,\n    category = AccountCategory,\n    action = CopyConfigurationAction,\n    label = None,\n    value = None)\n\n  val synchronizeConfigurationEvent = TrackEvent(\n    screen = ProfileScreen,\n    category = AccountCategory,\n    action = SynchronizeConfigurationAction,\n    label = None,\n    value = None)\n\n  val changeConfigurationNameEvent = TrackEvent(\n    screen = ProfileScreen,\n    category = AccountCategory,\n    action = ChangeConfigurationNameAction,\n    label = None,\n    value = None)\n\n  val deleteConfigurationEvent = TrackEvent(\n    screen = ProfileScreen,\n    category = AccountCategory,\n    action = DeleteConfigurationAction,\n    label = None,\n    value = None)\n\n  val showPublicationsContentEvent = TrackEvent(\n    screen = ProfileScreen,\n    category = PublicationCategory,\n    action = ShowPublicationsContentAction,\n    label = None,\n    value = None)\n\n  val addToMyCollectionsFromProfileEvent = TrackEvent(\n    screen = ProfileScreen,\n    category = PublicationCategory,\n    action = AddToMyCollectionsFromProfileAction,\n    label = Option(publication),\n    value = None)\n\n  val shareCollectionFromProfileEvent = TrackEvent(\n    screen = ProfileScreen,\n    category = PublicationCategory,\n    action = ShareCollectionFromProfileAction,\n    label = Option(publication),\n    value = None)\n\n  val showSubscriptionsContentEvent = TrackEvent(\n    screen = ProfileScreen,\n    category = SubscriptionCategory,\n    action = ShowSubscriptionsContentAction,\n    label = None,\n    value = None)\n\n  val subscribeToCollectionEvent = TrackEvent(\n    screen = ProfileScreen,\n    category = SubscriptionCategory,\n    action = SubscribeToCollectionAction,\n    label = Option(sharedCollectionId),\n    value = None)\n\n  val unsubscribeFromCollectionEvent = TrackEvent(\n    screen = ProfileScreen,\n    category = SubscriptionCategory,\n    action = UnsubscribeFromCollectionAction,\n    label = Option(sharedCollectionId),\n    value = None)\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/trackevent/SliderMenuTrackEventTestData.scala",
    "content": "package cards.nine.commons.test.data.trackevent\n\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\n\ntrait SliderMenuTrackEventTestData {\n\n  val goToCollectionsByMenuEvent = TrackEvent(\n    screen = SliderMenuScreen,\n    category = SliderOptionCategory,\n    action = GoToCollectionsByMenuAction,\n    label = None,\n    value = None)\n\n  val goToMomentsByMenuEvent = TrackEvent(\n    screen = SliderMenuScreen,\n    category = SliderOptionCategory,\n    action = GoToMomentsByMenuAction,\n    label = None,\n    value = None)\n\n  val goToProfileByMenuEvent = TrackEvent(\n    screen = SliderMenuScreen,\n    category = SliderOptionCategory,\n    action = GoToProfileByMenuAction,\n    label = None,\n    value = None)\n\n  val goToSendUsFeedbackEvent = TrackEvent(\n    screen = SliderMenuScreen,\n    category = SliderOptionCategory,\n    action = GoToSendUsFeedbackAction,\n    label = None,\n    value = None)\n\n  val goToHelpByMenuEvent = TrackEvent(\n    screen = SliderMenuScreen,\n    category = SliderOptionCategory,\n    action = GoToHelpAction,\n    label = None,\n    value = None)\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/trackevent/WidgetTrackEventTestData.scala",
    "content": "package cards.nine.commons.test.data.trackevent\n\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types.{AddedWidgetToMomentAction, AddedWidgetToMomentValue, WidgetScreen}\n\ntrait WidgetTrackEventTestData {\n\n  val momentEvent = TrackEvent(\n    screen = WidgetScreen,\n    category = momentCategory,\n    action = AddedWidgetToMomentAction,\n    label = Option(s\"$momentPackageName:$momentClassName\"),\n    value = Option(AddedWidgetToMomentValue))\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/data/trackevent/WizardTrackEventTestData.scala",
    "content": "package cards.nine.commons.test.data.trackevent\n\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\n\ntrait WizardTrackEventTestData {\n\n  val chooseAccountEvent = TrackEvent(\n    screen = WizardScreen,\n    category = WizardStartCategory,\n    action = ChooseAccountAction,\n    label = None,\n    value = None)\n\n  val chooseNewConfigurationEvent = TrackEvent(\n    screen = WizardScreen,\n    category = WizardConfigurationCategory,\n    action = ChooseNewConfigurationAction,\n    label = None,\n    value = None)\n\n  val chooseExistingDeviceEvent = TrackEvent(\n    screen = WizardScreen,\n    category = WizardConfigurationCategory,\n    action = ChooseExistingDeviceAction,\n    label = None,\n    value = None)\n\n  val chooseMomentEvent = TrackEvent(\n    screen = WizardScreen,\n    category = WizardMomentsWifiCategory,\n    action = ChooseMomentAction,\n    label = Option(\"OUT_AND_ABOUT\"),\n    value = None)\n\n  val chooseMomentWifiEvent = TrackEvent(\n    screen = WizardScreen,\n    category = WizardMomentsWifiCategory,\n    action = ChooseMomentWifiAction,\n    label = Option(\"OUT_AND_ABOUT\"),\n    value = None)\n\n  val chooseOtherMomentEvent = TrackEvent(\n    screen = WizardScreen,\n    category = WizardOtherMomentsCategory,\n    action = ChooseOtherMomentAction,\n    label = Option(\"MUSIC\"),\n    value = None)\n\n}\n"
  },
  {
    "path": "modules/commons-tests/src/main/scala/cards/nine/commons/test/repository/MockCursor.scala",
    "content": "package cards.nine.commons.test.repository\n\nimport java.lang.Math._\n\nimport android.database.Cursor\nimport org.mockito.Mockito._\nimport org.specs2.mock.Mockito\n\nsealed trait CursorDataType\n\ncase object ArrayByteDataType extends CursorDataType\n\ncase object DoubleDataType extends CursorDataType\n\ncase object FloatDataType extends CursorDataType\n\ncase object IntDataType extends CursorDataType\n\ncase object LongDataType extends CursorDataType\n\ncase object ShortDataType extends CursorDataType\n\ncase object StringDataType extends CursorDataType\n\ncase object BooleanDataType extends CursorDataType\n\ntrait MockCursor extends Mockito {\n\n  val mockCursor = mock[Cursor]\n\n  def mockIsAfterLast(size: Int) = {\n    val isAfterLastData = Seq.fill(size)(false) :+ true\n\n    when(mockCursor.isAfterLast).thenReturn(isAfterLastData.head, isAfterLastData.tail.toArray: _*)\n  }\n\n  def mockMoveToFirst(nonEmpty: Boolean) = when(mockCursor.moveToFirst).thenReturn(nonEmpty)\n\n  def mockMoveToNext(size: Int) = {\n    val sizeNext         = max(0, size - 1)\n    val isMoveToNextData = Seq.fill(sizeNext)(true) :+ false\n\n    when(mockCursor.moveToNext)\n      .thenReturn(isMoveToNextData.head, isMoveToNextData.tail.toArray: _*)\n  }\n\n  def mockGetColumnIndex(columnName: String, index: Int) =\n    when(mockCursor.getColumnIndex(columnName)).thenReturn(index)\n\n  def mockGetData(index: Int, valueSeq: Seq[Any], cursorDataType: CursorDataType) =\n    cursorDataType match {\n      case ArrayByteDataType =>\n        val arrayByteList = valueSeq.collect { case item: Array[Byte] => item }\n        when(mockCursor.getBlob(index))\n          .thenReturn(arrayByteList.head, arrayByteList.tail.toArray: _*)\n      case DoubleDataType =>\n        val doubleList = valueSeq.collect { case item: Double => item }\n        when(mockCursor.getDouble(index)).thenReturn(doubleList.head, doubleList.tail.toArray: _*)\n      case FloatDataType if valueSeq.nonEmpty =>\n        val floatList = valueSeq.collect { case item: Float => item }\n        when(mockCursor.getFloat(index)).thenReturn(floatList.head, floatList.tail.toArray: _*)\n      case IntDataType if valueSeq.nonEmpty =>\n        val intList = valueSeq.collect { case item: Int => item }\n        when(mockCursor.getInt(index)).thenReturn(intList.head, intList.tail.toArray: _*)\n      case LongDataType if valueSeq.nonEmpty =>\n        val longList = valueSeq.collect { case item: Long => item }\n        when(mockCursor.getLong(index)).thenReturn(longList.head, longList.tail.toArray: _*)\n      case ShortDataType if valueSeq.nonEmpty =>\n        val shortList = valueSeq.collect { case item: Short => item }\n        when(mockCursor.getShort(index)).thenReturn(shortList.head, shortList.tail.toArray: _*)\n      case StringDataType if valueSeq.nonEmpty =>\n        val stringList = valueSeq.collect { case item: String => item }\n        when(mockCursor.getString(index)).thenReturn(stringList.head, stringList.tail.toArray: _*)\n    }\n\n  def prepareCursor[T](size: Int, data: Seq[(String, Int, Seq[Any], CursorDataType)]) = {\n\n    mockCursor.getCount returns size\n    mockIsAfterLast(size = size)\n    mockMoveToFirst(nonEmpty = size > 0)\n    mockMoveToNext(size = size)\n\n    data foreach {\n      case (column, index, valueSeq, cursorDataType) if valueSeq.isEmpty =>\n        mockGetColumnIndex(column, index)\n      case (column, index, valueSeq, cursorDataType) =>\n        mockGetColumnIndex(column, index)\n        mockGetData(index, valueSeq, cursorDataType)\n    }\n  }\n\n  def booleanToInt(b: Boolean): Int = if (b) 1 else 0\n\n}\n"
  },
  {
    "path": "modules/docs/src/main/resources/microsite/css/custom.css",
    "content": "@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700');\n\n/* body\n/* -----------------------------------------*/\n\nbody {\n    font-family: 'Roboto', sans-serif;\n}\n\n#site-header .navbar-wrapper {\n  margin-top: 20px;\n}\n\n#site-header .navbar-wrapper nav ul li.gitter a i {\n  margin-right: 0;\n}\n\n#site-header .navbar-wrapper nav ul li.gitter a i img {\n  width: 24px;\n  height: 24px;\n}\n\n\n/* jumbotron\n/* -----------------------------------------*/\n\n.jumbotron {\n    text-align: center;\n    padding-top: 80px;\n}\n\n.jumbotron img {\n    max-width: 123px;\n    height: auto;\n}\n\n.jumbotron h1 {\n    font-weight: 300;\n}\n\n.jumbotron h1 span {\n    font-weight: 500;\n}\n\n.jumbotron .jumbotron-btn {\n    padding: 0;\n    margin: 0;\n}\n\n.jumbotron .jumbotron-btn li {\n    list-style: none;\n    display: inline-block;\n    margin-right: 10px;\n}\n\n.jumbotron .jumbotron-btn li:last-child {\n    margin-right: 0;\n}\n\n.jumbotron .btn {\n    padding: 0 15px;\n    display: flex;\n    align-items: center;\n}\n\n.jumbotron .btn i {\n    font-size: 22px;\n    margin-right: 8px;\n}\n\n.jumbotron .GitHub-link {\n    padding: 0;\n    margin: 30px 0 0 0;\n    text-transform: none;\n    font-size: 18px;\n}\n\n.jumbotron .GitHub-link li {\n    list-style: none;\n    display: inline-block;\n    margin-right: 18px;\n}\n\n.jumbotron .GitHub-link li a {\n    color: #fff;\n}\n\n.jumbotron .GitHub-link li a:hover {\n    text-decoration: none;\n    color: rgba(255, 255, 255, 0.5);\n}\n\n.jumbotron .GitHub-link li.dot {\n    font-size: 12px;\n    color: rgba(255, 255, 255, 0.4);\n}\n\n.jumbotron .GitHub-link li:last-child {\n    margin-right: 0;\n}\n\n.jumbotron .GitHub-link li i {\n    margin-right: 6px;\n}\n\n\n/* section\n/* -----------------------------------------*/\n\n.users {}\n\n.developers {\n    background: #00BCD4;\n}\n\n.users .carousel {\n    padding-top: 50px;\n}\n\n.users video {\n    margin-bottom: -8px;\n}\n\n.developers .carousel {}\n\n.carousel .carousel-control {\n    background: none;\n    text-shadow: none;\n    font-size: 60px;\n    color: #283593;\n    top: 40%;\n}\n\n\n\n.slide-img {\n    text-align: center;\n}\n\n\n.slide-text {\n    padding-top: 30px;\n}\n\n.developers .title-head {\n  position: absolute;\n  left: 0;\n  top: 100px;\n  width: 100%;\n  z-index: 200;\n}\n\n.developers .title-head h2 {\n    font-size: 32px;\n    color: #283593;\n    margin-bottom: 60px;\n\n}\n\n.developers .title-head h2 span {\n    padding-bottom: 10px;\n    border-bottom: 4px solid #283593;\n}\n\n.developers .slide-text h3 {\n  margin-top: 220px;\n}\n\n.slide-text h3 {\n    font-size: 23px;\n    color: #283593;\n    font-weight: 500;\n    margin-bottom: 28px;\n    margin-top: 100px;\n}\n\n.slide-text p {\n    font-size: 18px;\n}\n\n.developers .slide-text {\n\n\n}\n\n.developers .slide-text p {\n    color: #fff;\n}\n\n.developers .bg-image1 {\n    width: 100%;\n    height: 100%;\n    background: rgba(0, 188, 212, 0.9);\n    position: relative;\n\n}\n\n.developers .bg-image1:before {\n    content: \"\";\n    position: absolute;\n    background: url(\"../img/photo1.png\") no-repeat center center;\n    -webkit-background-size: cover;\n    -moz-background-size: cover;\n    -o-background-size: cover;\n    background-size: cover;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    z-index: -1;\n}\n\n.developers .bg-image2 {\n    width: 100%;\n    height: 100%;\n    background: rgba(0, 188, 212, 0.9);\n    position: relative;\n\n}\n\n.developers .bg-image2:before {\n    content: \"\";\n    position: absolute;\n    background: url(\"../img/photo2.png\") no-repeat center center;\n    -webkit-background-size: cover;\n    -moz-background-size: cover;\n    -o-background-size: cover;\n    background-size: cover;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    z-index: -1;\n}\n\n.developers .bg-image3 {\n    width: 100%;\n    height: 100%;\n    background: rgba(0, 188, 212, 0.9);\n    position: relative;\n\n}\n\n.developers .bg-image3:before {\n    content: \"\";\n    position: absolute;\n    background: url(\"../img/photo3.png\") no-repeat center center;\n    -webkit-background-size: cover;\n    -moz-background-size: cover;\n    -o-background-size: cover;\n    background-size: cover;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    z-index: -1;\n}\n\n.developers .carousel-inner {\n    height: 640px;\n}\n\n.developers .carousel, .developers .item, .developers .active {\n    height: 100%;\n}\n\n.logo-libraries {\n  border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n  padding-bottom: 30px;\n}\n\n.logo-libraries ul {\n  padding: 0;\n  margin: 0;\n  display: flex;\n  justify-content: space-around;\n  flex-wrap: wrap;\n}\n\n.logo-libraries ul li {\n  list-style: none;\n  margin-right: 40px;\n}\n\n.logo-libraries ul li:last-child {\n  margin-right: 0;\n}\n\n.logo-libraries img {\n  width: 40px;\n  height: 40px;\n  opacity: 0.2;\n}\n\n.logo-libraries img:hover {\n  opacity: 1;\n}\n\n\n/* media-queries\n/* -----------------------------------------*/\n\n\n@media screen and (min-width: 2400px) {\n\n  .carousel-control.left {\n    left: 10%;\n  }\n\n  .carousel-control.right {\n    right: 10%;\n  }\n\n}\n\n@media screen and (max-width: 2400px) {\n\n  .carousel-control.left {\n    left: 5%;\n  }\n\n  .carousel-control.right {\n    right: 5%;\n  }\n\n}\n\n\n@media screen and (max-width: 1800px) {\n\n  .carousel-control.left {\n    left: 0;\n  }\n\n  .carousel-control.right {\n    right: 0;\n  }\n\n}\n\n\n@media screen and (max-width: 768px) {\n    .jumbotron {\n        padding-top: 82px;\n    }\n    .users video {\n        width: 100%;\n    }\n\n    .slide-text h3 {\n      margin-top: 0;\n    }\n\n    .logo-libraries ul li {\n      margin-bottom: 20px;\n    }\n\n}\n"
  },
  {
    "path": "modules/docs/src/main/resources/microsite/data/menu.yml",
    "content": "options:\n  - title: Introduction\n    url: docs/index.html\n    menu_type: docs\n    menu_section: docs\n\n  - title: Libraries\n    url: docs/Libraries.html\n    menu_type: docs\n    menu_section: docs\n\n  - title: Client\n    url: docs/Client.html\n    menu_type: docs\n    menu_section: client\n\n    nested_options:\n      - title: Install Client\n        url: docs/client/Installation.html\n        menu_section: client\n      - title: Client Architecture\n        url: docs/client/Architecture.html\n        menu_section: client\n      - title: Database\n        url: docs/client/Database.html\n        menu_section: client\n      - title: Cloud Storage\n        url: docs/client/CloudStorage.html\n        menu_section: client\n\n  - title: Server\n    url: docs/Server.html\n    menu_type: docs\n    menu_section: server\n\n    nested_options:\n      - title: Install Server\n        url: docs/server/Installation.html\n        menu_section: server\n      - title: Server Architecture\n        url: docs/server/Architecture.html\n        menu_section: server\n      - title: Endpoints\n        url: docs/server/Ednpoints.html\n        menu_section: server\n      - title: Authentication\n        url: docs/server/Authentication.html\n        menu_section: server\n      - title: Cache\n        url: docs/server/Cache.html\n        menu_section: server\n"
  },
  {
    "path": "modules/docs/src/main/resources/microsite/includes/ninecards-footer.html",
    "content": "<footer id=\"site-footer\">\n    <div class=\"container\">\n        <div class=\"row\">\n            <div class=\"col-xs-6\">\n                <p>{{ site.name }} is designed and developed by <a href=\"http://www.47deg.com\" target=\"_blank\">47 Degrees</a></p>\n            </div>\n            <div class=\"col-xs-6\">\n                <p class=\"text-right\"><a href=\"https://github.com/47deg/nine-cards-v2\"><span class=\"fa fa-github\"></span>View on Github</a></p>\n            </div>\n        </div>\n    </div>\n</footer>\n"
  },
  {
    "path": "modules/docs/src/main/resources/microsite/includes/ninecards-header.html",
    "content": "<header id=\"site-header\">\n  <div class=\"navbar-wrapper\">\n    <div class=\"container\">\n      <div class=\"row\">\n        <div class=\"col-xs-12\">\n          <nav class=\"text-right\">\n            <ul>\n              <li class=\"gitter\">\n                <a href=\"https://gitter.im/47deg/nine-cards-v2\">\n                  <i>\n                    <img src=\"./img/gitter-logo.png\" alt=\"\">\n                  </i>\n                  <span class=\"hidden-xs\">Gitter channel</span>\n                </a>\n              </li>\n              <li>\n                <a href=\"docs/\">\n                  <i class=\"fa fa-file-text\"></i>\n                  <span class=\"hidden-xs\">Documentation</span>\n                </a>\n              </li>\n            </ul>\n          </nav>\n        </div>\n      </div>\n    </div>\n  </div>\n        <div class=\"jumbotron\">\n            <div class=\"container\">\n                <img src=\"./img/jumbotron_brand2x.png\" alt=\"\">\n                <h1 class=\"text-center\">An <span>Open Source</span> Android Launcher built with <span>Scala on Android</span></h1>\n                <ul class=\"jumbotron-btn\">\n                    <li><a href=\"https://github.com/47deg/nine-cards-v2\" class=\"btn btn-outline-inverse\"><i class=\"mdi mdi-github-circle\"></i>Source code on GitHub</a></li>\n                    <li><a href=\"https://play.google.com/store/apps/details?id=com.fortysevendeg.ninecardslauncher\" class=\"btn btn-outline-inverse\"><i class=\"mdi mdi-google-play\"></i>Download it on Google Play</a></li>\n                    <li><a href=\"https://youtu.be/x8F4ErrrbcI\" class=\"btn btn-outline-inverse\"><i class=\"mdi mdi-youtube-play\"></i>Watch the video</a></li>\n                </ul>\n                <!--<ul class=\"GitHub-link\">-->\n                    <!--<li><a href=\"#\"><i class=\"fa fa-eye\"></i>45</a></li>-->\n                    <!--<li><a href=\"#\"><i class=\"fa fa-star\"></i>65</a></li>-->\n                    <!--<li><a href=\"#\"><i class=\"fa fa-code-fork\"></i>234</a></li>-->\n                <!--</ul>-->\n            </div>\n        </div>\n    </header>\n"
  },
  {
    "path": "modules/docs/src/main/resources/microsite/layouts/ninecards-home.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n    <meta charset=\"utf-8\">\n    <meta content=\"IE=edge,chrome=1\" http-equiv=\"X-UA-Compatible\">\n    <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n    <title>9 Cards, an Open Source Android Launcher built with Scala on Android</title>\n    <meta content=\"9 Cards, an Open Source Android Launcher built with Scala on Android\" name=\"description\">\n    <meta name=\"keywords\" content=\"Open Source, Scala on Android, Scala project, Functional Programming, Functional Architecture\">\n\n    <meta content=\"{{site.url}}{{site.baseurl}}/img/poster.png \" name=\"og:image\">\n    <meta content=\"9 Cards, an Open Source Android Launcher built with Scala\" name=\"og:title \">\n    <meta content=\"9 Cards, an Open Source Android Launcher built with Scala on Android\" name=\"og:site_name \">\n    <meta content=\"http://www.9cards.io\" name=\"og:url \">\n    <meta content=\"website\" name=\"og:type\">\n    <meta content=\"9 Cards, an Open Source Android Launcher built with Scala on Android\" name=\"og:description \">\n\n    <meta content=\"{{site.url}}{{site.baseurl}}/img/poster.png\" name=\"twitter:image \">\n    <meta content=\"summary_large_image \" name=\"twitter:card \">\n    <meta content=\"@9CardsLauncher\" name=\"twitter:site \">\n\n    <link href=\"./img/favicon.png \" rel=\"icon \" type=\"image/png \">\n    <link href=\"https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css \" rel=\"stylesheet \">\n    <link href=\"https://cdn.materialdesignicons.com/1.8.36/css/materialdesignicons.min.css \" rel=\"stylesheet \">\n\n    <link href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/color-brewer.min.css \" rel=\"stylesheet \">\n    <link href=\"./highlight/styles/dracula.css \" rel=\"stylesheet \">\n    <link href=\"./css/style.css \" rel=\"stylesheet \">\n    <link href=\"./css/palette.css \" rel=\"stylesheet \">\n    <link href=\"./css/custom.css \" rel=\"stylesheet \">\n</head>\n<body>\n  {% include ninecards-header.html %}\n  <!-- Section -->\n      <section class=\"users\">\n          <div id=\"carousel-example-generic-user\" class=\"carousel slide\" data-ride=\"carousel\">\n              <div class=\"carousel-inner\" role=\"listbox\">\n                  <div class=\"item active\">\n                      <div class=\"container\">\n                          <div class=\"row\">\n                              <div class=\"col-md-6\">\n                                  <div class=\"slide-img\">\n                                      <video autoplay loop>\n                                        <source src=\"img/gallery1.webm\" type=\"video/webm\">\n                                        <source src=\"img/gallery1.mp4\" type=\"video/mp4\">\n                                      </video>\n                                  </div>\n                              </div>\n                              <div class=\"col-md-6\">\n                                  <div class=\"slide-text\">\n                                      <h3>It’s easier to find apps when they are grouped by Collections</h3>\n                                      <p>9Cards categorizes your apps into smart Collections. You also can explore different Collections created and shared by other users in order to discover new ways to use your smartphone</p>\n                                  </div>\n                              </div>\n                          </div>\n                      </div>\n                  </div>\n                  <div class=\"item\">\n                      <div class=\"container\">\n                          <div class=\"row\">\n                              <div class=\"col-md-6\">\n                                  <div class=\"slide-img\">\n                                    <video autoplay loop>\n                                      <source src=\"img/gallery2.webm\" type=\"video/webm\">\n                                      <source src=\"img/gallery2.mp4\" type=\"video/mp4\">\n                                    </video>\n                                  </div>\n                              </div>\n                              <div class=\"col-md-6\">\n                                  <div class=\"slide-text\">\n                                      <h3>Every moment of the day is different using Moments</h3>\n                                      <p>Your phone use changes throughout the day, that’s why 9 Cards shows widgets by Moments that are adapted to your daily routine. Whether you like to play games when you’re at home, listen to music on your commute, or check email during work, 9 Cards will learn your habits and launch the apps you want, when you want them</p>\n                                  </div>\n                              </div>\n                          </div>\n                      </div>\n                  </div>\n                  <div class=\"item\">\n                      <div class=\"container\">\n                          <div class=\"row\">\n                              <div class=\"col-md-6\">\n                                  <div class=\"slide-img\">\n                                      <video autoplay loop>\n                                          <source src=\"img/gallery3.webm\" type=\"video/webm\">\n                                          <source src=\"img/gallery3.mp4\" type=\"video/mp4\">\n                                      </video>\n                                  </div>\n                              </div>\n                              <div class=\"col-md-6\">\n                                  <div class=\"slide-text\">\n                                      <h3>Apps are important, your Contacts are too</h3>\n                                      <p>Our App Drawer’s behavior is focused on your productivity. With a simple gesture, you can access your contacts, filter your apps by recently installed, or see your missed calls. You can also search directly in Google Play</p>\n                                  </div>\n                              </div>\n                          </div>\n                      </div>\n                  </div>\n              </div>\n              <a class=\"left carousel-control\" href=\"#carousel-example-generic-user\" role=\"button\" data-slide=\"prev\">\n                  <span class=\"mdi mdi-chevron-left\" aria-hidden=\"true\"></span>\n                  <span class=\"sr-only\">Previous</span>\n              </a>\n              <a class=\"right carousel-control\" href=\"#carousel-example-generic-user\" role=\"button\" data-slide=\"next\">\n                  <span class=\"mdi mdi-chevron-right\" aria-hidden=\"true\"></span>\n                  <span class=\"sr-only\">Next</span>\n              </a>\n          </div>\n      </section>\n\n      <!-- Develops -->\n      <section class=\"developers\">\n          <div id=\"carousel-example-generic-dev\" class=\"carousel slide\" data-ride=\"carousel\">\n              <div class=\"carousel-inner\" role=\"listbox\">\n\n                <div class=\"title-head\">\n                  <div class=\"container\">\n                    <div class=\"row\">\n                      <div class=\"col-md-8 col-md-offset-2\">\n                      <h2><span>For developers</span></h2>\n                      </div>\n                    </div>\n                  </div>\n                </div>\n                  <div class=\"item active\">\n                      <div class=\"bg-image1\">\n                          <div class=\"container\">\n                              <div class=\"row\">\n                                  <div class=\"col-md-8 col-md-offset-2\">\n                                      <div class=\"slide-text\">\n                                          <h3>Functional Programing in the client side</h3>\n                                          <p>The 9 Cards app was developed with Scala on Android and emphasizes a Functional Programming style over the available imperative Android SDK. 9 Cards doesn’t use common Java Android libraries for views; instead, all views are based on Macroid for Functional UI composition and the business logic in the client is built atop a simple monad transformer stack in Scala</p>\n                                      </div>\n                                  </div>\n                              </div>\n                          </div>\n                      </div>\n                  </div>\n                  <div class=\"item\">\n                      <div class=\"bg-image2\">\n                          <div class=\"container\">\n                              <div class=\"row\">\n                                  <div class=\"col-md-8 col-md-offset-2\">\n                                      <div class=\"slide-text\">\n                                          <h3>A modern architecture in the Backend using Free Monads</h3>\n                                          <p>The server app architecture is based on free monads. A Free Monad is a data type that allows the decoupling of program declaration from program interpretation. This means you can use Free monads and Free applicatives to completely describe a program’s business logic</p>\n                                      </div>\n                                  </div>\n                              </div>\n                          </div>\n                      </div>\n                  </div>\n                  <div class=\"item\">\n                      <div class=\"bg-image3\">\n                          <div class=\"container\">\n                              <div class=\"row\">\n                                  <div class=\"col-md-8 col-md-offset-2\">\n                                      <div class=\"slide-text\">\n                                          <h3>9 Cards would not be possible without Scala Libraries</h3>\n                                          <p>We are using some of the most popular Scala libraries in the client and backend, for instance: Cats, Monix, Doobie, scalaZ, scalacheck and so on, and frameworks such as Spray on the server side. Our goal has been to create a similar architecture for the client and server using these amazing libraries of the Scala Ecosystem</p>\n                                      </div>\n                                  </div>\n                              </div>\n                          </div>\n                      </div>\n                  </div>\n              </div>\n              <a class=\"left carousel-control\" href=\"#carousel-example-generic-dev\" role=\"button\" data-slide=\"prev\">\n                  <span class=\"mdi mdi-chevron-left\" aria-hidden=\"true\"></span>\n                  <span class=\"sr-only\">Previous</span>\n              </a>\n              <a class=\"right carousel-control\" href=\"#carousel-example-generic-dev\" role=\"button\" data-slide=\"next\">\n                  <span class=\"mdi mdi-chevron-right\" aria-hidden=\"true\"></span>\n                  <span class=\"sr-only\">Next</span>\n              </a>\n          </div>\n      </section>\n      <section class=\"technologies\">\n          <div class=\"container\">\n            <div class=\"logo-libraries\">\n              <ul>\n                <li><a href=\"https://www.scala-lang.org/\"><img src=\"./img/scala-logo.png\" alt=\"\" /></im></a></li>\n                <li><a href=\"http://typelevel.org/cats/\"><img src=\"./img/cat-logo.png\" alt=\"\" /></im></a></li>\n                <li><a href=\"https://monix.io/\"><img src=\"./img/monix-logo.png\" alt=\"\" /></im></a></li>\n                <li><a href=\"http://47deg.github.io/macroid/\"><img src=\"./img/macroid-logo.png\" alt=\"\" /></im></a></li>\n                <li><a href=\"http://spray.io/\"><img src=\"./img/spray-logo.png\" alt=\"\" /></im></a></li>\n                <li><a href=\"http://akka.io/\"><img src=\"./img/akka-logo.png\" alt=\"\" /></im></a></li>\n                <li><a href=\"https://github.com/tpolecat/doobie\"><img src=\"./img/doobie-logo.png\" alt=\"\" /></im></a></li>\n                <li><a href=\"https://github.com/milessabin/shapeless\"><img src=\"./img/shapeless.png\" alt=\"\" /></im></a></li>\n                <li><a href=\"https://circe.github.io/circe/\"><img src=\"./img/circe-logo.png\" alt=\"\" /></im></a></li>\n              </ul>\n            </div>\n          </div>\n          <div class=\"container\">\n              <div class=\"row\">\n                  <div class=\"col-md-4\">\n                      <div class=\"first-icon-wrapper\"></div>\n                      <h3>Android</h3>\n                      <p>A customizable Launcher for Android that adapts to your daily routine</p>\n                  </div>\n                  <div class=\"col-md-4\">\n                      <div class=\"second-icon-wrapper\"></div>\n                      <h3>Scala</h3>\n                      <p>9 Cards is the first open source, fully integrated Launcher for Android written in Scala</p>\n                  </div>\n                  <div class=\"col-md-4\">\n                      <div class=\"third-icon-wrapper\"></div>\n                      <h3>Open Source</h3>\n                      <p>By opening its development, we hope that the community helps us to expand and improve this\n                          project</p>\n                  </div>\n              </div>\n          </div>\n      </section>\n\n  {% include ninecards-footer.html %}\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js\"></script>\n    <script src=\"./highlight/highlight.pack.js\"></script>\n    <script>\n        $('.carousel').carousel({\n            interval: false\n        });\n    </script>\n</body>\n</html>\n"
  },
  {
    "path": "modules/docs/src/main/tut/CNAME",
    "content": "www.9cards.io"
  },
  {
    "path": "modules/docs/src/main/tut/docs/Client.md",
    "content": "---\nlayout: docs\ntitle: Client\nsection: docs\n---\n\nThis describes the different features of the 9 Cards Client:\n\n* [Install Client](client/Installation.html)\n* [Client Architecture](client/Architecture.html)\n* [Database](client/Database.html)\n* [Cloud Storage](client/CloudStorage.html)"
  },
  {
    "path": "modules/docs/src/main/tut/docs/Libraries.md",
    "content": "---\nlayout: docs\ntitle: Libraries\nsection: docs\n---\n\n# Client Stack\n\n* [Scala](https://www.scala-lang.org/): we are using Scala instead of Java\n* [Cats](http://typelevel.org/cats/): a library which provides abstractions for functional programming in Scala\n* [Monix](https://monix.io/): a high-performance Scala library for composing asynchronous and event-based programs\n* [SBT Android Plugin](https://github.com/scala-android/sbt-android): SBT plugin for compiling Scala on Android\n* [Macroid](http://47deg.github.io/macroid/): DSL for Android to create user interfaces in a functional way with Scala Macros\n* [Android SDK](https://developer.android.com/guide/index.html)\n\n# Server Stack\n\n* [Spray](http://spray.io/): an open-source toolkit for building REST/HTTP-based integration layers on top of Scala and Akka\n* [Akka](http://akka.io/): a toolkit and runtime for building highly concurrent, distributed, and resilient message-driven applications\n* [Circe](https://circe.github.io/circe/): JSON library for Scala\n* [Doobie](http://github.com/tpolecat/doobie): a pure functional JDBC layer for Scala\n* [Http4s](http://http4s.org/): a typeful, purely functional HTTP library for client and server applications written in Scala\n* [Cats](http://typelevel.org/cats/): a library which provides abstractions for functional programming in Scala\n* [Monix](https://monix.io/): a high-performance Scala library for composing asynchronous and event-based programs\n* [Flyway](https://flywaydb.org/): an open-source database migration tool\n* [Typesafe config](https://typesafehub.github.io/config/): a configuration library for JVM languages\n* [Shapeless](http://github.com/milessabin/shapeless) is used as a utility in several places in the code, and is relied upon by the libraries `Circe`, `ScalaCheck`, and `Doobie`"
  },
  {
    "path": "modules/docs/src/main/tut/docs/Server.md",
    "content": "---\nlayout: docs\ntitle: Server\nsection: docs\n---\n\nThis describes the different features of the 9 Cards Server:\n\n* [Install Server](server/Installation.html)\n* [Server Architecture](server/Architecture.html)\n* [Endpoints](server/Endpoints.html)\n* [Authentication](server/Authentication.html)\n* [Cache](server/Cache.html)"
  },
  {
    "path": "modules/docs/src/main/tut/docs/client/Architecture.md",
    "content": "---\nlayout: docs\ntitle: Client Architecture\nsection: docs\n---\n\n# Architecture\n\nOur Activities, Fragment, and Other screen of Android’s call-to-action use Jobs. Jobs are a group of methods that contain the things that the UI can do. For example, `loadItems`, `showItem`, `markAsDone`, etc. \n\nThe principle of Jobs is that they can connect to the UI (using UI Actions) and API, repository, or anything else (using Services).\n\nThe architecture is divided into **three layers**:\n\n- **Services**: This module contains services for connecting to out of the applications. For example: API, Repository, Disk, so on\n- **Process**: This module contains the uses cases which access the services layer to create semantic methods for the app, for example, get collections, sync device, add an application, etc.\n- **Android**: This module contains the Android SDK with Activities, Fragments, and so on, used in the project. Every screen has jobs, that allows us to combine UI actions and calls to our processes.\n\nOur architecture is based on two Typelevel libraries: Cats and Monix. These are the main libraries we use to create a functional architecture in our project. 9 Cards does not use Android libraries for views; the project only uses the Android SDK, and all views have been created in Scala.\n\n![architecture](/img/9cards_architecture.png)\n\nIn order to be able to compose the methods of the UI and Services, all methods must return the same type. The type is define in the commons module:\n\n```scala\ntype TaskService[A] = EitherT[Task, NineCardException, A]\n```\n\nOur TaskService type is a Task of **Monix** in order to async tasks and uses a EitherT of **Cats** for exceptions and value of the method.\n\nFor example, a method of our Job can have calls to both the UI and Processes:\n \n```scala\n  def loadWidgets(): TaskService[Unit] =\n    for {\n      _ <- actions.showLoading()\n      widgets <- di.deviceProcess.getWidgets\n      _ <- actions.loadWidgets(widgets)\n    } yield ()\n```\n\nWe can do that in the activity:\n\n```scala\nwidgetsDialogJobs.loadWidgets().resolveAsyncServiceOr(_ => showErrorLoadingWidgetsInScreen())\n```\n   \nWe can also compose the methods of different jobs using _Applicative_ in **Cats**: \n \n```scala\nimport cats.implicits._\n\n(widgetsJobs.hostWidget(widget) *> widgetsDialogJobs.close()).resolveAsync()\n``` "
  },
  {
    "path": "modules/docs/src/main/tut/docs/client/CloudStorage.md",
    "content": "---\nlayout: docs\ntitle: Cloud Storage\nsection: docs\n---\n\n# Cloud Storage\n\nWe store a user's configurations in their **Google Drive**. In order to do this, we need to have Google Service Permission. If the user agrees, we store their collections, dock apps, moments, and other specifications in their Drive.\n\n## What user information do we store?\n\nWe store the following information:\n\n1. **Device and Document Info**: the name of the device, Android identifier, and the version of the document.\n2. **Collections**: All collections that the user has on their cell phone. Every collection contains the name of the collection, color, type, icon, information related to a shared collection (if it's necessary), and the items of each collection (apps, contacts, and shortcuts).\n3. **Moments**: Information related to Moments (timestamp, location, wifi, etc.) when a collection and associated widgets of 9 Cards shows on the home screen.\n\n## JSON Structure in Google Drive\n\nNames of the fields and types of the structure that we store in Google Drive:\n\n### CloudStorageDevice\n\n```\n{\n  \"deviceId\": String,\n  \"deviceName\": String,\n  \"documentVersion\": Int,\n  \"collections\": Seq[CloudStorageCollection],\n  \"moments\": Seq[CloudStorageMoment],\n  \"dockApps\": Seq[String] // List of package names of applicactions\n}\n```\n\n### CloudStorageCollection\n\n```\n{\n  \"name\": String,\n  \"icon\": String,\n  \"category\": Option[String],\n  \"collectionType\": String,\n  \"originalSharedCollectionId\": Option[String],\n  \"sharedCollectionId\": Option[String],\n  \"sharedCollectionSubscribed\": Option[Boolean],\n  \"items\": Seq[CloudStorageCollectionItem],\n  \"moment\": Option[CloudStorageMoment]\n}\n```\n\n### CloudStorageCollectionItem\n\n```\n{\n  \"title\": String,\n  \"itemType\": String,\n  \"intent\": String\n}\n```\n\n### CloudStorageMoment\n\n```\n{\n  \"timeslot\": Seq[CloudStorageMomentTimeSlot],\n  \"wifi\": Seq[String],\n  \"headphones\": Boolean\n}\n```\n\n### CloudStorageMomentTimeSlot\n\n```\n{\n  \"from\": String,\n  \"to\": String,\n  \"days\": Seq[Int]\n}\n```\n"
  },
  {
    "path": "modules/docs/src/main/tut/docs/client/Database.md",
    "content": "---\nlayout: docs\ntitle: Database\nsection: docs\n---\n\n# Database\n\n**Table of Contents**\n\n* [1. Collections](#collections)\n  * [1.1. Fields](#fields)\n* [2. Cards](#cards)\n  * [2.1. Fields](#fields-1)\n* [3. Apps](#apps)\n  * [3.1. Fields](#fields-2)\n* [4. DockApps](#dockapps)\n  * [4.1. Fields](#fields-3)\n* [5. Moments](#moments)\n  * [5.1. Fields](#fields-4)\n* [6. Users](#users)\n  * [6.1. Fields](#fields-5)\n* [7. Widgets](#widgets)\n  * [7.1. Fields](#fields-6)\n\n## 1. Collections\n\nThis table stores all the information related to the collections created in 9 Cards:\n\n### 1.1. Fields\n\n* Position: `Int` - The position of the collection within the app\n* Name: `String` - The name of the collection\n* Type: `String` - The type of elements that the collection contains (e.g., apps, contacts...)\n* Icon: `String` - The icon of the collection\n* AppsCategory: `String` - The name of the category in Google Play to which the collection is associated\n* OriginalSharedCollectionId: `String` - Id of the original collection shared by another user\n* SharedCollectionId: `String` - Id assigned to the collection when it's shared with other users\n* SharedCollectionSubscribed: `Boolean` - Field which indicates if the user wants to receive notifications when the shared collection changes\n\n## 2. Cards\n\nThis table stores all of the information related to the cards created in 9 Cards. Each card could represent an app, a phone number, an email address, etc.\n\n### 2.1. Fields\n\n* Position: `Int` - Position of the card within the collection\n* CollectionId: `Int` - The foreign key of the collection to which the card is associated\n* Term: `String` - The name which describes the element represented by the card\n* PackageName: `String` - If the card represents an app, the package name of the app\n* Type: `String` - The type of element that the card represents (e.g., app, phone, email, etc.)\n* Intent: `String` - If the card represents an app, the intent which will be launched when the app starts\n* ImagePath: `String` - The path of the icon shown inside the card\n* Notification: `String` - Generic text used for showing notifications\n\n## 3. Apps\n\nThis table stores all apps installed in a user's cellphone:\n\n### 3.1. Fields\n\n* Name: `String` - Name of the app\n* PackageName: `String` - Package name of the application\n* ClassName: `String` - Main class in order to launch the app\n* Category: `String` - Category of the app in Google Play\n* dateInstalled: `Long` - Date installed\n* dateUpdate: `Long` - Date updated\n* version: `String` - Version of the app\n* installedFromGooglePlay: `Boolean` - If the app was installed from Google Play\n\n## 4. DockApps\n\nThis table stores the apps that a user adds to the bottom dock in the launcher:\n\n### 4.1. Fields\n\n* Name: `String` - Name of the app\n* PackageName: `String` - Package name of the application\n* DockType: `String` - The kind of dock (e.g., apps, contacts, etc.)\n* Intent: `String` - The intent which will be launched when the app starts\n* imagePath: `String` - The path of the icon shown inside the card\n* position: `Int` - Position of the card within the dock layout\n\n## 5. Moments\n\nThis table stores the list of a user's moments and the information contained within them:\n\n### 5.1. Fields\n\n* CollectionId: `String` - The foreign key of  the collection to which the card is associated\n* Timeslot: `String` - Information about the time that the moment will be activated\n* WiFi: `String` - List of the WiFi network and password that will be activated for each moment\n* MomentType: `String` - The type of moment such as home, night, work, gym, etc.\n\n## 6. Users\n\nThis table stores the information about the app's user:\n\n### 6.1. Fields\n\n* Email: `String` - Email of the user\n* ApiKey: `String` - API key of the user that is used in the backend\n* SessionToken: `String` - Session token of the user for the backend\n* DeviceToken: `String` - Device token of the user for the backend\n* MarketToken: `String` - Market token of the user for the backend\n* Name: `String` - Name of the user in Google Plus\n* Avatar: `String` - Avatar of the user in Google Plus\n* Cover: `String` - Cover of the user in Google Plus\n* DeviceName: `String` - Device name that the user is using in Google Drive\n* DeviceCloudId: `String` - Device Cloud Id that the user is using in Google Drive\n\n## 7. Widgets\n\nThis table stores the widgets that the user has added to each moment:\n\n### 7.1. Fields\n\n* MomentId: `Int` - The foreign key of the moment to which the widget is associated\n* PackageName: `String` - Package name of the application of the widget\n* ClassName: `String` - Main class of the widget selected\n* AppWidgetId: `Int` - Android Widget ID given by the AppWidget provider\n* StartX: `Int` - Position where the widget starts in X\n* StartY: `Int` - Position where the widget starts in Y\n* SpanX: `Int` - Number of positions where the widget is expanded in X\n* SpanY: `Int` - Number of positions where the widget is expanded in Y\n* widgetType: `String` - Type of widget (e.g: app)\n* label: `String` - Label of the widget for a specific type of widget\n* imagePath: `String` - Image path of the widget for a specific type of widget\n* intent: `String` -  Intent of a widget for a specific type of widget"
  },
  {
    "path": "modules/docs/src/main/tut/docs/client/Installation.md",
    "content": "---\nlayout: docs\ntitle: Install Client\nsection: docs\n---\n\n# Installation\n\nPrerequisites:\n\n## SBT\n\n* [Download](http://www.scala-sbt.org/download.html) and install sbt\n\n## Android SDK\n\n* [Download](https://developer.android.com/studio/index.html#downloads). You only need the command line tools.\n* Set the `ANDROID_HOME` environment variable to point to the root folder.\n\n## Android Device\n\nYou need an Android device and must [enable USB debugging](https://www.google.es/search?q=android+activate+developer+mode&oq=android+active+developer).\n\n## Google Project\n\n9 Cards needs the following Google APIs:\n\n* Google Drive API: for storing a user's devices in the cloud\n* Google Plus API: for authenticating the user requests\n\nYou need to create a project in the Google API Console with these two APIs enabled.\n\nFor that, you have two choices: \n\n* Normal Mode (Recommended): You must create the keys in Google Developer Console. You only need 10 minutes to complete this.\n* Easy Mode: We give you the keys, and you don't have to create the project in the Google Developer Console.\n\n\n## Normal Mode: Google Project\n\n1. Go to [Google Developer Console](https://console.developers.google.com/apis/library?project=_)\n2. From the Project drop-down, select a [project](https://support.google.com/cloud/answer/6158853), or create a new one.\n\n### Google Drive API\n\n1. Enable the Google Drive API service:\n    1. In the sidebar under \"API Manager,\" select *Library*.\n    2. In the list of Google APIs, search for the Google Drive API service.\n    3. Select Google Drive API from the results list.\n    4. Press the Enable API button.\n2. In the sidebar under \"API Manager,\" select Credentials.\n3. In the Credentials tab, select the *Create credentials* drop-down list, and choose OAuth client ID.\n4. Select *Android* as the *Application type*.\n5. Enter a key Name.\n6. [Find your certificate SHA1 fingerprint](https://developers.google.com/android/guides/client-auth) and paste it in the form where requested.\n7. Enter `com.fortysevendeg.ninecardslauncher` in the package name field.\n8. Click on \"Create.\"\n\n[More info](https://developers.google.com/drive/android/auth)\n\n### Google Plus API\n\n1. Enable the Google Plus API service:\n    1. In the sidebar under \"API Manager,\" select *Library*.\n    2. In the list of Google APIs, search for the Google+ API service.\n    3. Select Google+ API from the results list.\n    4. Press the Enable API button.\n2. In the sidebar under \"API Manager,\" select *Credentials*.\n3. In the Credentials tab, select the *Create credentials* drop-down list, and choose OAuth client ID.\n4. Select *Web application* as the *Application type*.\n5. Enter a key Name then select Create.\n6. Then copy the *client ID* of the newly generated credential.\n\n\n## Easy Mode: Google Project\n\nThe only thing you need to do is add the following content to `ninecards.properties` file:\n\n```\n# Backend V2\nbackend.v2.url=https://nine-cards-stage.herokuapp.com\nbackend.v2.clientid=411191100294-sjhinp1i2gkp46u36ii7m16v9hog64nn.apps.googleusercontent.com\n```\n\nand you must launch SBT with the following command:\n\n```\n$ sbt -mem 2048 -Ddebug\n```\n\nAt the end of the compilation, previously used for installing on a cellphone, you must put the password of the keystore:\n\n```\nEnter keystore password:\n```\n\nThe password is `android`\n\nNote: If you're interested in working on this project, please consider using the `Default Mode`.\n\n# Compile and Run\n\nTo compile the project:\n\n* Clone this GitHub project to your computer:\n\n```\n$ git clone git@github.com:47deg/nine-cards-v2.git\n```\n\n* Add a `ninecards.properties` file (See [Add Debug Keys](#properties-file) section)\n\n* You need to set the heap size to at least 2M:\n\n```\n$ sbt -mem 2048\n```\n\n* Verify that your device is attached:\n\n```\n> devices\n```\n\nThe output should look like:\n\n```\n[info]  Serial                 Model            Battery % Android Version\n[info]  ---------------------- ---------------- --------- ---------------\n[info]  XXXXXXXXXX             Nexus 6                66% 6.0.1  API 23\n```\n\n* Now you're ready to run the project, just execute:\n\n```\n> run\n```\n\n# Properties File\n\nYou need to add a `ninecards.properties` file in the project root folder. \n\nThis file provides various keys for different third-party services. We'll see this below:\n\nTo begin with, you can use the template provided in the root folder:\n\n```\n$ cp ninecards.properties.default ninecards.properties\n```\n\nThis is the content:\n\n```\n# Backend V2\nbackend.v2.url=\nbackend.v2.clientid=\n\n# Third Parties\ncrashlytics.enabled=false\ncrashlytics.apikey=\ncrashlytics.apisecret=\nstrictmode.enabled=false\nanalytics.enabled=false\nanalytics.trackid=\n\n# Firebase\nfirebase.enabled=false\nfirebase.url=\nfirebase.google.appid=\nfirebase.google.apikey=\nfirebase.gcm.senderid=\nfirebase.clientid=\n```\n\n## Backend V2 (Mandatory)\n\n* `backend.v2.url`: Defines the URL for the Backend. Visit the [GitHub project](https://github.com/47deg/nine-cards-backend) for more information.\n* `backend.v2.clientid`: This value is used for requesting a token id that will be used by the Backend to authenticate the user. It's the *client id* obtained in the [Google Plus API section](#google-plus-api). \n\n## Third-Parties (Optional)\n\n**[Crashlytics](https://try.crashlytics.com/)**\n\n* `crashlytics.enabled`: Enables or disables the Crashlytics service\n* `crashlytics.apikey` & `crashlytics.apisecret`: These values are fetched from your [Crashlytics organization page](https://www.fabric.io/settings/organizations)\n\n**[Strict Mode](https://developer.android.com/reference/android/os/StrictMode.html)**\n\n* `strictmode.enabled`: Enables or disables the Strict Mode\n\n**[Google Analytics](https://developers.google.com/analytics/)**\n\n* `analytics.enabled`: Enables or disables the Google Analytics service\n* `analytics.trackid`: You can use your own tracking ID. See how to [find your tracking code, tracking ID, and property number](https://support.google.com/analytics/answer/1032385)\n\n## Google Firebase (Optional)\n\nGoogle Firebase is used for push notifications.\n\n**[Google Firebase](https://firebase.google.com/)**\n\n1. Create a Firebase project in the [Firebase console](https://firebase.google.com/console/), if you don't already have one. If you already have an existing Google project associated with your mobile app, click Import Google Project. Otherwise, click Create New Project.\n2. Add a new app in *Project Settings* -> *General*\n3. Select the newly created app and download the `google-services.json`\n4. Open the file in a text editor. All properties listed below are taken from this file: \n\n* `firebase.enabled`: Enables or disables the Google Firebase service\n* `firebase.url`: Property `project_info.firebase_url`\n* `firebase.google.appid`: Property `client[0].client_info.mobilesdk_app_id`\n* `firebase.google.apikey`: Property `client[0].api_key[0].current_key`\n* `firebase.gcm.senderid`: Property `project_info.project_number`\n* `firebase.clientid`: Property `client[0].oauth_client[x].client_id` where x is the index of one element with `client_type` == 3\n"
  },
  {
    "path": "modules/docs/src/main/tut/docs/index.md",
    "content": "---\nlayout: docs\ntitle: Introduction\nsection: docs\n---\n\n# Introduction\n\nIn 2014 we created 9 Cards App 1.0, a new Launcher concept for Android applications. 9 Cards is a customizable Home Launcher designed to improve your Android experience by organizing your apps and adapting to your daily routine. \n\nFor 9 Cards 2.0, we have three primary goals:\n\n * Use the Scala programming language, instead of Java in order to provide the community with a real application built in Scala with a user base of thousands. \n * Use a functional approach in a world where most people work in an imperative way.\n * Use a similar architecture in both the client-side and the backend.\n"
  },
  {
    "path": "modules/docs/src/main/tut/docs/server/Architecture.md",
    "content": "---\nlayout: docs\ntitle: Server Architecture\nsection: docs\n---\n\n# Architecture\n\nThis file describes the architecture of the 9 Cards Backend.\n\n## Overview\n\nIn this section, we will describe the environment of the application, and how it interacts with this environment.\n\n### API\n\nThe 9 Cards backend is a HTTP/REST application. Every operation is initiated by a call to an endpoint in the API. The backend has no other internal state changing operations.\nMost of the endpoints in the API are assumed to receive messages from a 9 Cards client.\nAll the information sent from the backend to any client is sent in response to a client's HTTP request.\nThe backend does not communicate directly with 9 Cards' clients. For notifications, it uses\nthe [Firebase Cloud Messaging](http://firebase.google.com/docs/cloud-messaging/) API.\n\n\n### External Systems\n\nThe backend application interacts with several **external** systems and APIs outside of the application, for retrieving or storing information, or for sending notifications. The external systems are the following:\n\n* **Database**. A [PostgreSQL](http://www.postgresql.org/) database is used to store information that's needed by the backend.\n  This includes information about 9 Cards' clients (users and devices), shared collections, the user's subscriptions of shared collections, and the information about countries.\n* **Google API**. The Google API is used upon a client's signup, to check the identity of a new client.\n* **Google Analytics**. A Google Analytics report collects aggregated statistics about the applications used in 9 Cards, such as which applications are more frequently added or removed to a _moment_, or which applications are most frequently used within each category. This data doesn't contain any information about the user with the exception of the country, as given by Google Analytics.\n  From this data, the backend builds the _rankings_ of applications.\n* **Android Market**. The API of the Android Market is used to retrieve information about the Android applications in the Play store, and also for searching lists of new applications either by name, by their category, or by their similarity to apps that are already installed.\n* **Play Store Web**. Sometimes, the information about an app is not available in the Market API; instead, we retrieve it from each app's page in the [Play Store website](http://play.google.com/store/).\n* **Redis**. We use a [Redis](http://redis.io/) in-memory store as a _cache_ to keep non-original data; that is to say, data that is retrieved from a remote source, or computed from a remote source. This includes the data about each application fetched from the Android Market API, and the most-valued application rankings computed from the Google Analytics Report.\n* **Firebase**. The [Firebase Cloud Messaging](http://firebase.google.com/docs/cloud-messaging/) API is used to notify the subscribers of any changes to the list of apps in a shared collection.\n\n### Core Modules\n\nThe backend source code is organized into four modules, or `sbt` projects, called `api`, `processes`, `services`, `googleplay`, and `commons`.\n\n* The [`commons`](/modules/commons) module contains the definition of the backend's `domain` classes,\n  and utility methods for common libraries, such as `cats` or `scalaz`. It also handles the environment configuration variables needed for each service interpreter.\n* The [`api`](/modules/api) module implements the `REST-HTTP` application's API, and the application's\n  main method. It uses the libraries `spray`, and `spray-json` (with a bit of `circe`).\n* The [`processes`](/modules/processes) module contains the application's process methods. Each process method implements the functionality of one endpoint, using one or more services. The implementation follows a functional programming style, based on the use of monadic operations.\n* The [`services`](/modules/services) module implements the backend's services; each services retrieves and\n  communicates data with an external system. The services layer is based on the `doobie` and `http4s` libraries.\n* The [`googleplay`](/modules/googleplay) implements a subset of services, originally developed as a separate\n  module that interacts with the Android Market API, and which uses the Redis cache for internal storage.\n\nThe `commons` module is available to all other modules. The dependencies among other modules are as follows:\nthe `api` only depends on `processes`; the `processes` only depends on `services`, and `services` depends\non `googleplay`.\n\n## Functional Programming Design\n\nThis section describes the Functional Programming design followed in writing the processes and services for the backend. This design is inspired by the [Data Types à la carte](http://dblp.org/rec/html/journals/jfp/Swierstra08) paper. The key elements of this design are _1)_ a separation between a service's algebra of operations and\nits interpreter, and _2)_ the use of the `Free` monad for combining operations of different algebras.\n\n### Services: Algebra and Interpreter\n\nEach service in the `services` layer declares an algebra of operations, and an interpreter of those operations.\n\nThe **algebra** defines an abstract data type `Ops[A]`, which represents the operations that can be done in that service.\nEach operation can be seen as a [command](http://en.wikipedia.org/wiki/Command_pattern) object,\nwhich carries the input parameters needed to specify the value of the result.\nAs a guideline, each operation in an algebra should be atomic and involve at the most, one interaction\nwith the external system in question.\n\nThe **interpreter** is the part of the service that takes the operations of the algebra and computes the results of that operation.\nAn `Interpreter` is a (class of) object that implements a function from the service's algebra ADT\n`Ops[A]` to another parametric type, `F[A]`, with the same base type `A`.\nOperations that transform from one parametric type `F[A]` to another parametric type `G[A]` are called _natural transformations_;\nand in the backend, every service interpreters must be a subtype of [`cats.arrow.FunctionK`](http://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/arrow/FunctionK.scala).\n\nThe target type `F` of the interpretation represents the kind of computation involved to obtain that result.\nIt can be [`Task`](http://github.com/scalaz/scalaz/blob/series/7.3.x/concurrent/src/main/scala/scalaz/concurrent/Task.scala),\nto represent an asynchronous computation of the result,\nor [`ConnectionIO`](http://github.com/tpolecat/doobie/blob/master/core/src/main/scala/doobie/free/connection.scala),\nto represent a computation that performs queries with `doobie`,\nor just `Id`, to represent a result obtained just within the application (for testing).\n\n#### Processes: Free Monad and Coproducts\n\nEach process in the processes layer implements the functionality of one endpoint, and it usually involves\nusing different services.\nFor instance, the process to retrieve the details of a shared collection involves reading the collection information\nout of the database and then retrieving from the Android Market API (or from the cache) the data of each\napplication in that collection. However, each service defines its operation in a separate algebra,\nsuch as `OpsDB[A]` or `OpsRK[B]`.\n\nTo combine operations from several services, each operation of each algebra is _injected_ into a `Free` monad\n`Free[F,A]`. If the `F` type parameter is the same, then the\n[`Free`](http://github.com/typelevel/cats/blob/master/free/src/main/scala/cats/free/Free.scala) datatype provides\nthe operations for passing the results between the operations from different algebras.\n\nThis is achieved by using as that `F`, a `Coproduct` of the different algebras operation types `Ops[A]`.\nThe interpretation of the `Coproduct` operations, then, becomes an alternative between them."
  },
  {
    "path": "modules/docs/src/main/tut/docs/server/Authentication.md",
    "content": "---\nlayout: docs\ntitle: Authentication\nsection: docs\n---\n# Authentication and Authorization\n\nThe 9 Cards Backend Application authenticates almost all of the endpoints.\n\n# Client Signup in the Backend\n\nWe'll now describe the process of interactions performed for a client (user and device) to sign-up in the NCBE.\nIn essence, this process is just a third-party authentication (the third party being the NCBE) following the [OAuth 2.0 protocol](https://developers.google.com/identity/protocols/OAuth2).\nIt consists of an interaction between the NCBE, the **Google Account Services** (GAC), and the **Client**, which is the 9 Cards Android application running from the user's device.\n\n1. **Grant Google Account Permissions.**\n   The *Client* sends a request to the GAC to open an authorization token. The request carries the user's `email` and the `deviceId`,\n   which uniquely identifies the instance of the NineCardsClient issuing the request.\n   If the request is accepted, the GAC responds with success and includes in the response a `tokenId`, which identifies a short-lived OAuth session within the GAC.\n\n2. **Client signup in NCBE.**\n   The *Client* sends a HTTP request to the NCBE's endpoint `POST {apiRoot}/login`, to register it.\n   This request carries (as a JSON object in the body entity) three fields:\n   * the `email` that serves as an external identifier for the user running the client\n   * the `androidId`, that identifies the Android device in which the user is running the client application\n   * the `tokenId` that the client received from the GAC in Step 1.\n\n   If the request succeeds, the NCBE records the client's user and device and returns a response to the client app.\n   The response carries (in a JSON object in the request body) two fields: a `sessionToken` and an `apiKey`.\n   * The `sessionToken` is a unique identifier of the user within the NCBE instead of the user's email.\n   * The `apiKey` is the private cryptographic key that the client uses after signup to authenticate in the NCBE.\n\n3. **NCBE access to GAC.**\n   To carry out the process of endpoint `POST {apiRoot}/login`, the NCBE communicates to the GAC to validate the `tokenId` from the request body.\n   If the `tokenId` is validated, the GAC returns a successful response.\n   \n# User Authentication\n\nAll NCBE endpoints, except the one to read the API documentation and the one to signup,\ncarry out an authentication step to check that the client app sending the requests is acting for a registered user.\nThe information for user authentication is carried in the HTTP headers `X-Android-ID`, `X-Auth-Token`, and `X-Session-Token`.\n\n* The `X-Android-ID` should give the `androidId` of the client's device. Note that, since the GAC process in the signup\n  involves a user and device, it is the device itself and not just the user that should be signed up beforehand.\n\n* The `X-Session-Token` should carry the user's `sessionToken` that the NCBE generated and gave to the client in Step 3 of the signup process.\n  This value acts as a way to identify the user within the NCBE.\n\n* The `X-Auth-Token` is a [Hash-based message authentication code](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code), which is used for authenticating the request.\n  It ensures that the client which is using the `sessionToken` is the one that is acting for that user.\n\nThe value of the `X-Auth-Token` header is computed as follows.\nThe *message* to be signed is just the full [URI](https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.2.2) of the request, including protocol, host, port, path, and query.\nThe *cryptographic key* used is the user's `apiKey`, which the NCBE generates and gives to the client (with the `sessionToken`) at the end of the signup process.\nThe code is then calculated using the `sha512` hash function.\nTo calculate the value, you can use one of these methods:\n\n* The command `openssl` can generate the HMAC digest of a message. For example, to digest the URI `http://localhost:8080/collections/a` with the key `foo`, you would run:\n\n        echo -n \"http://localhost:8080/collections/a\" | openssl dgst -sha512 -hmac \"foo\" | awk '{print $2}'\n\n* This [web page](http://www.freeformatter.com/hmac-generator.html) offers a graphic dialog to generate the digest.\n  The request URL would be put into the `message` dialog, the user's `apiKey` would go into the `SecretKey` text box,\n  and the algorithm would be \"SHA512\". The result would be the digest.\n\n# Manually obtaining a TokenId\n\nSometimes, you may want to manually carry out the first step of the signup, which is the communication between the\nClient and the Google Account Services.\n\nGoogle provides an [`OAuth 2.0 Playground`](https://developers.google.com/oauthplayground/), which can be used to generate a Google ID Token.\nWe use it to generate an OAuth token that, within the Google API, allows us to read the information needed for the client.\nThis information is the user's email, so the scope used is `https://www.googleapis.com/auth/userinfo.email`.\n\n1. Open the [OAuth 2.0 Playground page](https://developers.google.com/oauthplayground/).\n   On this page, look in the lateral pane for the menu `Google+ API v1`, from this menu, mark the scope `https://www.googleapis.com/auth/userinfo.email`,\n   and push the `Authorize APIs` button.\n2. If you have several Google accounts stored in the browser, you may be asked to select one. You will then\n   be presented with a Google permissions dialog, asking you to allow an application to _Read your email address_.\n    Press _Allow_.\n3. After pressing _Allow_, the playground page will change to a new view. The modified page shows you `Request/Response`.\n   In the left pane there is a text field labeled _Authorization Code_, and a button labeled _Exchange authorization code for tokens_.\n   Press this button. Google OAuth playground then generates a new token, which is shown in the API response in the right pane, in a field named as _id_token_.\n   This _id_token_ identifies a session within the _Google Account Services_, which is to expire within the hour.\n4. Copy the value of the _id_token_ generated. A request to login endpoint `POST {apiRoot}/login` should include a JSON object with the field `tokenId` in the body. The value of this field should be the _id_token_."
  },
  {
    "path": "modules/docs/src/main/tut/docs/server/Cache.md",
    "content": "---\nlayout: docs\ntitle: Cache\nsection: docs\n---\n\n# Cache\n\nIn an effort to reduce the number of requests to Google Play API, we store the Google Play info on the app in an intermediate cache using REDIS. \n\n\n##Redis cache management\n\nRedis cache contains only one key per package and can store four types of values depending on the results of getting information from Google Play:\n- `resolved`: Those packages for which the process of getting Google Play info has been successfully completed will have this tag.\n- `pending`: If while the execution of a multi-package request, the Google Play API returns an error (`401 Unauthorized` or `429 Too many requests`), all the packages without info will be tagged as pending. An internal process will try to get the info later.\n- `error`: If the request to get the Google Play information returns a `404 Not Found` error, the package will be tagged as error.\n- `permanent`: All the packages with this tag are known applications that are not published in Google Play, but we can categorize them (for instance, the Samsung camera).\n\nThe format of the key and value will be:\n- **Key:** `\"com.package.name:TYPE\"`\n- **Value:**\n  - Example of a `resolved` item:\n    \n```\n{\n    \"packageName\": \"com.package.name\",\n    \"title\": \"Package title\",\n    \"free\": true,\n    \"icon\": \"http://lh3.googleusercontent.com/aYbdIM1abwyVSUZLDKoE0CDZGRhlkpsaPOg9tNnBktUQYsXflwknnOn2Ge1Yr7rImGk\",\n    \"stars\": 4.5,\n    \"downloads\": \"500,000,000 - 1,000,000,000\",\n    \"categories\": [\"SOCIAL\"],\n    \"screenshoots\": [\"url1\", \"url2\"]\n}\n```\n  - Example of a `pending` item:\n    \n```\n{}\n```\n  - Example of an `error` item with the dates when the errors were produced:\n    \n```\n[\n    \"date1\",\n    \"date2\"\n] \n```\n  - Example of a `permanent` item:\n    \n```\n{\n    \"packageName\": \"com.package.name\",\n    \"categories\": [\"SOCIAL\"]\n}\n```\n\n## Workflow\n\nThe described workflow will be performed for each package of the list:\n\n1. Check if a key exists in Redis for the package and the value type is either `resolved` or `permanent`.\n   - If the key/value exists, the process will return the stored package info as resolved.\n   - Otherwise, the process will continue in step 2.\n   \n2. Try to get the package info by using Google Play API.\n   - If the API returns a valid response:\n     - A new item of type `resolved` will be created in Redis.\n     - If a previous `error` or `pending` item exists, this key will be removed.\n     - The process will return the package info as resolved.\n     - If an error is thrown (like `401 Unauthorized` or `429 Too many requests`), the process will continue in step 3.\n     \n3. Check if the package exists in Google Play by requesting the server headers (the response will be quick and smaller in size).\n   - If the package exists, the process will continue in step 4.\n   - Otherwise, the process will continue in step 5.\n   \n4. Check if a key exists in Redis for the package and the type of the value is either `pending` or `error`.\n   - If a value type of `pending` exists, the process will do nothing.\n   - If a value type of `error` exists, the process will change the type of the item to `pending`.\n   - Otherwise, a new item of type `pending` will be created in Redis.\n   - In all cases, the package will return as `pending`.\n   \n5. Check if a key exists in Redis for the package and the value type is `error`.\n   - If the key exists, a new date will be appended to the value.\n   - Otherwise a new item of type `error` will be created in Redis.\n   - In all cases, the package will return as `error`.\n"
  },
  {
    "path": "modules/docs/src/main/tut/docs/server/Ednpoints.md",
    "content": "---\nlayout: docs\ntitle: Endpoints\nsection: docs\n---\n\n# 9 Cards Backend V2: List of Endpoints\n\nThis file gives a description of the endpoints in the 9 Cards Backend API.\nThe 9 Cards Backend is a server-side HTTP/REST application, which supports the\nactions of the [9 Cards launcher](https://github.com/47deg/nine-cards-v2),\nprovides it with needed information, and implements the communication across\nseveral clients through the use of shared collections.\n\n<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-generate-toc again -->\n**Table of Contents**\n\n- [9 Cards Backend V2, List of Endpoints](#ninecards-backend-v2-list-of-endpoints)\n    - [Glossary](#glossary)\n    - [Headers](#headers)\n        - [Client Authentication Headers](#client-authentication-headers)\n            - [Client Authentication failure](#client-authentication-failure)\n            - [Google Play Token Header](#google-play-token-header)\n    - [Body Entity Objects](#body-entity-objects)\n        - [Application Cards](#application-cards)\n        - [Shared Collection Card](#shared-collection-card)\n        - [Shared Collection List](#shared-collection-list)\n    - [Endpoints](#endpoints)\n        - [Users and installations](#users-and-installations)\n            - [User Signup](#user-signup)\n            - [Update an installation](#update-an-installation)\n        - [Application endpoints](#application-endpoints)\n            - [Categorize apps](#categorize-apps)\n            - [Get Apps Details](#get-apps-details)\n            - [Rank Applications](#get-ranking-of-applications)\n        - [Recommendation Endpoints](#recommendation-endpoints)\n            - [Recommend by list of apps](#recommend-by-list-of-apps)\n            - [Recommend by category](#recommend-by-category)\n        - [Collection endpoints](#collection-endpoints)\n            - [Publish a shared collection](#publish-a-shared-collection)\n            - [Read a collection](#read-a-collection)\n            - [Edit a collection](#edit-a-collection)\n            - [Collections published by user](#collections-published-by-user)\n            - [List of collections by category](#list-of-collections-by-category)\n        - [Subscription endpoints](#subscription-endpoints)\n            - [List of subscribed collections](#list-of-subscribed-collections)\n            - [Subscribe to a shared collection](#subscribe-to-a-shared-collection)\n            - [Unsubscribe from a shared collection](#unsubscribe-from-a-shared-collection)\n        - [Rankings Endpoints](#rankings-endpoints)\n            - [Read a Ranking](#read-a-ranking)\n            - [Refresh a Ranking](#refresh-a-ranking)\n\n<!-- markdown-toc end -->\n\n## Glossary\n\nThe headers and data fields within the backend server refer to several concepts of the\napplication's domain, involving the Google Play Store for Android applications.\n\n* An **Android user** is a user account on the Android platform, which may be within\n  one or more Android devices. The user account is identified with an email address.\n* An **Android ID** is a globally unique ID, issued for each device running Android.\n* A **client** refers to both the Android user account and the Android Id of a device\n  running the [9 Cards launcher](https://github.com/47deg/nine-cards-v2).\n* A **device token** identifies a notification mailbox, unique for each client (user account and device).\n  The backend uses the device token to notify a client of any updates to a public collection they're subscribed to. This is done through [Firebase notification API](https://firebase.google.com/).\n  Note that a _client_ may have no _device token_ associated with it.\n* A **package name** (or _package_ for short) is a unique identifier for each Android app.\n  The syntax of package names is like that of Java packages. It is a dot-separated sequence of\n  one or more segments, where each segment is a lowercase word.\n  For instance, the [YouTube app](https://play.google.com/store/apps/details?id=com.google.android.youtube)\n  has `com.google.android.youtube` as its package name.\n* A **category** within the Google Play Store is a name for a group of applications that solve\n  similar needs. Syntactically, a category is a list of underscore-separated upper-case words,\n  such as `SOCIAL` or `GAME_ACTION`.\n  Categories are not exclusive so an app may belong to several categories.\n\n\n\n## Headers\n\nThe Backend API requires in its request, some standard or non-standard request headers.\nWe group these headers depending on the purpose they are used for.\n\n#### Standard Headers\n\nAll of the endpoints in this API that require either a body entity in the request\nor provide one in the response, use [JSON](https://tools.ietf.org/html/rfc7159) for serialization.\nThose endpoints will need the following standard content negotiation headers\n `Content-Type` (for the request) and `Accept` (for the response), with the value\n `application/json` in both headers.\n\n#### Client Authentication Headers\n\nThese headers are used to authenticate the sender, and to identify the user account\nand the Android device that corresponds to this server.\n\n* `X-Android-ID`: should give the Android Id of the client's device. Note that, since the\n  process in the signup involves a user and device, not only the user but the device as well should be signed up beforehand.\n* `X-Session-Token`: should carry the user's `sessionToken` that the backend generated and gave to the client\n  in Step 3 of the signup process (see the README).\n  This value acts as a way to identify the user within the backend.\n* `X-Auth-Token`: the [Hash-based message authentication code](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code),\n  used for authenticating the request. It ensures that the client using the `sessionToken` is the one that is acting for that user.\n\nYou can find more details about these headers in the `README`.\n\n##### Client Authentication failure\nMost endpoints in the backend API require the client authentication headers,\nto identify the sender as a client, on whose account the endpoint operates.\nThose endpoints fail with a `401 Unauthorized` status code if\n_a)_ any of the authentication headers are missing; or\n_b)_ the `X-Session-Token` or the `X-Android-ID` do not correspond to a client signed up in the backend; or\n_c)_ the `X-Auth-Token` does not correspond with the result of hashing the request URL\n    with the API key that the backend gave that client.\n\n#### Google Play Token Header\n\nThe Google Play API is the main source of information on Android apps,\nand the backend uses it to issue requests to those APIs on behalf of the user.\nThe header `X-Google-Play-Token` is used to pass an access token for the Google Play API,\nwhich is issued by this API to each client.\nAccess to this API may sometimes fail because the given token is unauthorized or expired.\nIt may also fail if the request quota for that token is exhausted.\n\nAlthough the Google Play API is the main source of information, the backend uses a\ncache to store any information it has already fetched, and it sometimes uses\nthe public [web page of Play Store](https://play.google.com/store/) to retrieve it.\nFor this reason, the endpoints generally do not inform if the `X-Google-Play-Token`\nis unauthorized, or its quota is expired. They do, however, return a `401 Unauthorized`\nresponse status code if this header is missing.\n\nAlong with this header, it is possible to use the optional header `X-Android-Market-Localization`.\nThis one is used to identify the localization, language, and availability,\nfor which the information about each Android App should be fetched.\nValues for this header are [BCP-47 codes](https://tools.ietf.org/html/bcp47), such as `en-US` or `es-ES`.\n\n## Body Entity Objects\n\nAll of the body entities passed by the endpoints of this API, be it as requests or responses,\nare encoded in [JSON](https://tools.ietf.org/html/rfc7159).\nWhen describing the entities, we use the names of types and constants as specified in the standard,\nfor example _object_ means [JSON Object](https://tools.ietf.org/html/rfc7159#section-4).\n\n### Application Cards\n\nAn application card is a set of information about an Android app, that the backend fetches and gives back.\nThe information of an application card is provided by Google's Play store, either through its API or its web page.\nThe backend does not edit such information, only fetches it and stores it in a cache.\nThe fields in an application card object are usually some, or all of these:\n* `packageName`: a string with the _package name_ of the application.\n* `title`: a string with a human-readable name of the app.\n* `categories`: a list of strings, which are the _categories_ in which the app is listed.\n  These categories are usually sorted from greater to lesser _relevance_ to the app.\n* `category`: a string that gives the _first_ (thus most relevant) category the app is associated with.\n* `free`: a boolean value that indicates if the app is free or not.\n* `stars`: a floating-point number between `1.0` and `5.0`, with the average score given by the users of the app.\n* `downloads`: a string which gives an estimate of the number of times the app has been downloaded.\n    This estimate is sometimes a range, such as  `\"100-1000\"`, or `1000000+`.\n* `icon`: a string with the URL inside the Play store of the app's icon.\n* `screenshots`: a list of strings, each of which is a URL to a screenshot of the Google app execution.\n\nThe fields `category` and `categories` do not appear together. Here is an example of an application card:\n\n```json\n{\n  \"packageName\" : \"one.valid.package\",\n  \"title\" : \"The One Valid Package\",\n  \"free\"  : true,\n  \"stars\" : 3.52,\n  \"downloads\" : \"1.000.000.000 - 5.000.000.000\",\n  \"icon\" : \"https://lh5.ggpht.com/jZ8XCjpCQWWZ5GLhbjRA\",\n  \"categories\": [ \"SOCIAL\"]\n}\n```\n\n### Shared Collection Card\n\nA shared collection card contains the information about a shared collection that the backend manages.\nShared collections are stored and managed by the backend, so the endpoints can edit\nthe data contained within them.\n\nThe fields in a shared collection card are some, or all of these:\n\n* `publicIdentifier`: a string with the public identifier of the collection.\n* `publishedOn`: the time and date (in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time),\n  in which the collection was published. This time is written with the format `2013-11-23T05:07:13.109`.\n* `author`: a human-readable alias for the user who published the collection.\n* `category`: the category in which the collection falls.\n* `icon`: a string with the URL to the icon for this shared collection.\n* `community`: a boolean value to indicate if this is a community collection or not.\n* `packages`: a list of strings, that includes the _package names_ of every app in the collection.\n* `subscriptions` (optional field): the number of 9 Cards' users who have subscribed to this collection.\n* `installations`: the number of devices that have a shared collection installed.\n* `views`: the number of visits to the shared collection\n* `appsInfo`: a list of objects, where each object contains the information of one `apps` in the collection.\n  Each of these objects contains the fields `packageName`, `title`, `free`, `icon`, `stars`, `downloads`, and `categories`,\n  as described in [the previous section](#application-cards).\n\nThe following is an example of this type of object:\n\n```json\n{\n  \"publicIdentifier\" : \"sociappathy\",\n  \"publisedOn\" : \"2015-11-23T12:32:44.555\",\n  \"author\" : \"Jon Doe\",\n  \"name\" : \"Social Appathy\",\n  \"category\" : \"SOCIAL\",\n  \"icon\" : \"http://photo.blogs/url/of/icon\",\n  \"community\" : false,\n  \"installations\": 11,\n  \"subscriptions\": 12,\n  \"views\": 13,\n  \"packages\" : [ \"one.valid.package\", \"non.existing.app\" ],\n  \"appsInfo\" : [\n    {\n       \"packageName\" : \"one.valid.package\",\n       \"title\" : \"The One Valid Package\",\n       \"free\"  : true,\n       \"stars\" : 3.52,\n       \"downloads\" : \"1.000.000.000 - 5.000.000.000\",\n       \"icon\" : \"https://lh5.ggpht.com/jZ8XCjpCQWWZ5GL\",\n       \"categories\": [ \"SOCIAL\"]\n    }\n  ]\n}\n```\n\n### Shared Collection List\n\nA shared collection list carries several shared collections, given by the backend.\nAs a body entity, a shared collection list is an object with a single field,\n`collections`, which is a list of objects.\nEach of these objects is a [shared collection card](#shared-collection-card).\n\n```json\n{\n  \"collections\" : [\n    {\n      \"publicIdentifier\" : \"sociappathy\",\n      \"publisedOn\" : \"2015-11-23T12:32:44.555\",\n      \"author\" : \"\",\n      \"name\" : \"Social Appathy\",\n      \"category\" : \"SOCIAL\",\n      \"icon\" : \"http://photo.blogs/url/of/icon\",\n      \"community\" : false,\n      \"subscriptions\": 27564,\n      \"packages\" : [ \"one.valid.package\", \"non.existing.app\" ],\n      \"appsInfo\" : [\n        {\n           \"packageName\" : \"one.valid.package\",\n           \"title\" : \"The One Valid Package\",\n           \"free\"  : true,\n           \"stars\" : 3.52,\n           \"downloads\" : \"1.000.000.000 - 5.000.000.000\",\n           \"icon\" : \"https://lh5.ggpht.com/jZ8XCjpCQWWZ5\",\n           \"categories\": [ \"SOCIAL\"]\n        }\n      ]\n    }\n  ]\n}\n```\n\n\n## Endpoints\n\nThe specification of the endpoints follows the `REST` style of interfaces as a guideline.\nIn the description of each endpoint's functionality, we use _\"the client\"_ to refer\nto the sender of the HTTP request, which would usually be an instance of the 9 Cards Launcher.\nThe response status of most endpoints, in general, will be  a `200 OK` status code in case of success;\nor a `400 BadRequest` if the request is missing a needed entity body, or if this one is present but does\nnot fit the JSON schema outlined.\n\n### Users and installations\n\nThese endpoints serve to manage the information about client instances\nregistered within the server, and the communication between them.\n\n#### User Signup\n\nThe `POST /login` endpoint signs up a client (the sender) within the backend.\nIts operation reaches out to the Google API to check the identity of the client's user account.\nThe request entity body must be an object with the following fields:\n* `email`: a string that contains the email address of an Android user.\n  Its syntax should be that of an email address, and should always include the domain.\n* `androidId`: a string with the ID of the Android device in which the client instance is running.\n  Syntactically, it is an uppercase hexadecimal string.\n* `tokenId`: a string which contains an OAuth2 access token, issued by the Google OAuth server to the client.\n  The backend only uses this token for this endpoint, to check the identity of the client's user account.\n  It is not stored afterward.\n\nFor example, a valid request body would look like this:\n\n```json\n{\n  \"email\"     : \"jon.doe@gmail.com\",\n  \"androidId\" : \"1CAFE80C\",\n  \"tokenId\"   : \"k23.k1Li4iliMa_\"\n}\n```\n\nIf successful, the body response should be an object with the following fields:\n* `sessionToken`: a string containing the session token, as defined above,\n  that the backend uses as an alternative identity for the client.\n* `apiKey`: a string with the cryptographic key that the backend computes for the client.\n  The client uses this key to compute the value of the `X-Auth-Token` header for subsequent requests.\n\nHere is an example of a valid response:\n\n```json\n{\n  \"sessionToken\" : \"qwertyuiop90\",\n  \"apiKey\" : \"azerty40\"\n}\n```\n\n#### Update an installation\n\nThe endpoint `PUT /installations` allows editing the _device token_  associated to the client.\nThis client is identified through the [client authentication headers](#client-authentication-headers),\nwhich the request must provide.\nThe request body must be an object which may contain the optional field `deviceToken`.\nThis field's value is either a string, or `null`. Thus, each of these is a valid body:\n\n```json\n{ \"deviceToken\" : \"tokentoken\" }\n{ \"deviceToken\" : null }\n{}\n```\n\nThe action of this endpoint is to modify the `deviceToken` associated to the client.\nIf the `deviceToken` field in the request is a string, then this value replaces the previous one.\nIf it is `null`, or the field is missing, then the client is left with no `deviceToken`.\n\nIf successful, the response body is an object that contains two fields:\nthe `androidId` of the client that issued the request,\nand the `deviceToken` which that client has been assigned to.\nFor instance, a valid response would be the following one:\n\n```json\n{\n  \"androidId\" : \"1CAFE80C\",\n  \"deviceToken\" : \"tokentoken\"\n}\n```\n\nThe response status for this endpoint can be `200 OK`, if the request was correct and it was processed correctly;\nor `401 Unauthorized` if there was a [client authentication failure](#client-authentication-failure).\n\n\n### Application endpoints\n\nThese endpoints are used for querying data about Android apps, irrespective of the\nshared collections in which they may appear.\n\n\n#### Categorize apps\n\nThe `POST /applications/categorize` endpoint takes a list of app _package names_,\nclassifies them by each app's main categories, and reports for which apps it could\nnot find the category.\nThe request must include the [client authentication headers](#client-authentication-headers)\nand the [Google Play Token Header](#google-play-token-header).\nThe request body must be an object with one field `items`, whose value is a list of strings.\nEach one is a _package name_. An example would be the following one:\n\n```json\n{\n  \"items\" : [ \"com.google.android.youtube\", \"package.which.does.not.exists\" ]\n}\n```\n\nIf successful, the response body should be an object with the following fields:\n* `errors`: a list of strings, each of them a _package names_ from the request body.\n  This list contains the names of the apps which could not be categorized.\n* `items`: a list of objects, each of which corresponds to an Android app that was successfully\n  categorized. Each object has two fields, `packageName` and `category`, which are as described\n  [above](#application-cards). The `packageName` must be one listed in the request body.\n\nAn example would be the following response:\n\n```json\n{\n  \"errors\" : [ \"package.which.does.not.exists\" ],\n  \"items\" : [\n     {\n       \"packageName\" : \"com.google.android.youtube\",\n       \"category\" : \"VIDEO_PLAYERS\"\n     }\n  ]\n}\n```\nFailure to categorize an app might occur because the Google Play token is unauthorized, or its\nquota is exhausted. It may also be because a package name is not an app.\nIt may also be an app bundled by some Android vendors, which is not published in the Play Store.\nThese failures do not affect the response status for this endpoint.\nThe response status  can be `200 OK`, if the request was correct and it could be processed;\nor `401 Unauthorized`, either because of [wrong client authentication](#client-authentication-failure)\nor because the [Google Play Token Header](#google-play-token-header) is missing.\n\n\n#### Get Apps Details\n\nThe `POST /applications/details` endpoint takes a list of app _package names_\nand for each app, it gives back a _card_ with the details about that app.\nIt also reports those apps for which it could not find details.\nThe request must include the [client authentication headers](#client-authentication-headers)\nand the [Google Play Token Header](#google-play-token-header).\nThe body should be an object with a single field `items`,\nwhose value is a list of strings. Each string is the _package name_ of an Android app.\nThe following is an example:\n\n```json\n{\n  \"items\" : [ \"com.google.android.youtube\", \"package.which.does.not.exists\" ]\n}\n```\n\nIf successful, the response body is an object with two fields:\n* `errors`: a list of strings, each one being one of the _package names_\n  from the `items` field in the request body. This list contains the names of the apps for which it could not locate details.\n* `items`: a list of objects, where each object contains some of the details of an Android app.\n  These objects contain the fields `packageName`, `title`, `free`, `stars`, `downloads`, `icon`,\n  and `categories`, which are as described in the section for [application cards](#application-cards).\n  Note that the value of `packageName` should be one of the packages listed in the request body.\n\nHere is an example response:\n\n```json\n{\n  \"errors\" : [ \"package.which.does.not.exists\" ],\n  \"items\" : [\n    {\n      \"packageName\" : \"com.google.android.youtube\",\n      \"title\" : \"Youtube\",\n      \"free\"  : true,\n      \"stars\" : 4.231,\n      \"downloads\" : \"1.000.000.000 - 5.000.000.000\",\n      \"icon\" : \"https://lh5.ggpht.com/jZ8XCjpCQWWZ5GLh\",\n      \"categories\": [ \"VIDEO_PLAYERS\"]\n    }\n  ]\n}\n```\n\nThese failures do not affect the response status for this endpoint. This status can be\n `200 OK`, if the request was correct and it could be processed;\nor  `401 Unauthorized`, either because of wrong [client auth headers](#client-authentication-failure),\nor because the [Google Play Token Header](#google-play-token-header) is missing.\n\n\n\n#### Get Ranking of Applications\n\nThe `POST /applications/rank` endpoint takes several lists of package names as input,\nand returns each list with the apps sorted according to the app's popularity\namongst users of 9 Cards.\n\nThe request must include the [client authentication headers](#client-authentication-headers).\nThe request body must be an object with two fields:\n* `location`: an optional string which gives the geographic location of the client,\n  by giving the [two-letter code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 ISO-2) of the country in which the client device is located. This is used to choose a ranking with a geographic scope\n  (country, continent) that better fits the client.\n* `items`: an object that contains the lists of apps. Each of the fields in this object corresponds to a category, so the field key is the name of the category. The value of the field is the list of app package names that are to be ranked within that category.\n\nHere is an example of this body entity:\n\n```json\n{\n  \"location\" : \"ES\",\n  \"items\" : {\n    \"SOCIAL\" : [ \"com.facebook\", \"com.twitter\"],\n    \"VIDEO_PLAYER\" : [\"com.youtube\", \"com.vimeo\"]\n  }\n}\n```\n\nIf successful, the response body is an object with a single field `items`. The value of this field\nis an object like the request, whose keys are the categories, and whose values are the lists of app\npackage names, sorted by ranking from most valued to least valued by the users of 9 Cards.\n\n```json\n{\n  \"items\" : {\n    \"SOCIAL\" : [ \"com.facebook\", \"com.twitter\"],\n    \"VIDEO_PLAYER\" : [\"com.vimeo\", \"com.youtube\"]\n  }\n}\n```\n\n### Recommendation Endpoints\n\nThese endpoints are used by the client to search for more apps that may be of interest to the client's user.\nIn these endpoints, the backend is not using any information of its own regarding collections\nor rankings. It's just acting as an intermediary and cache for these results.\n\n#### Recommend by list of apps\n\nThe endpoint `POST /recommendations` can be used to recommend a client a list of\napps to install, based on those already installed.\nThe request must include the [client authentication headers](#client-authentication-headers)\nand the [Google Play Token Header](#google-play-token-header).\nThe request body must be an object with the following fields:\n* `packages`: a list of strings, each of them a package name. These are the\n   apps which are to be used as a reference for the recommendations.\n* `excludePackages`: a list of strings, each of them a package name. These are the packages\n  that should not appear in the list of recommendations in the response.\n* `limitPerApp`: an integer number, which says how many related packages should be\n  explored for each of the apps in `packages`.\n* `limit`: an integer number, that sets the maximum number of recommended elements\n  in the response.\n\nHere is an example of a request body:\n\n```json\n{\n  \"packages\" : [ \"one.package.lamp\", \"two.package.cat\"],\n  \"excludePackages\": [ \"the.bad.package\", \"once.upon.time\"],\n  \"limitPerApp\" : 4,\n  \"limit\" : 20\n}\n```\n\nIf this endpoint succeeds, then the response entity is an object with the only field\n`items`, which is a list of objects that represent the recommended apps.\nEach object has the fields `packageName`, `title`, `free`, `icon`, `stars`, `downloads`,\nand `screenshots`, as described in the [app card section](#application-cards).\nHere is an example of the response:\n\n```json\n{\n  \"items\" : [\n    {\n      \"packageName\" : \"one.valid.package\",\n      \"title\" : \"The One Valid Package\",\n      \"free\"  : true,\n      \"stars\" : 3.52,\n      \"downloads\" : \"1.000.000.000 - 5.000.000.000\",\n      \"icon\" : \"https://lh5.ggpht.com/icon999999\",\n      \"categories\": [ \"SOCIAL\"]\n    }\n  ]\n}\n```\n\nThe response status for this endpoints can be\n`200 OK`,  if the request was correct and it could be processed; or\nor  `401 Unauthorized`, either because of wrong [client auth headers](#client-authentication-failure),\nor because the [Google Play Token Header](#google-play-token-header) is missing.\n\n\n#### Recommend by category\n\nThe `POST /recommendations/{category}/{priceFilter}` endpoint is used to recommend a list of apps to install for a particular category to a client.\nThe endpoints take two parameters as path segments:\n* `category`, the category in which to look for recommended apps; and\n* `priceFilter`, which can be one of `FREE`, `PAID`, or `ALL`, which filters the\n  recommendations to include only free, paid, or all apps.\n\nThe request must include the [client authentication headers](#client-authentication-headers)\nand the [Google Play Token Header](#google-play-token-header).\nThe request body should be an object with two fields, `excludePackages` and `limit`,\nwhich are just like the ones described for [the previous endpoint](recommend-by-list-of-apps). \n\nHere is a request body example:\n\n```json\n{\n  \"excludePackages\": [ \"the.bad.package\", \"once.upon.time\"],\n  \"limit\" : 20\n}\n```\n\nIf successful, the response entity is just like that of the\n[previous endpoint](#recommend-by-list-of-apps).\nThe response status for this endpoint can be\n`200 OK`,  if the request was correct and it could be processed; or\n`404 NotFound`, if either the `category` is not a valid category name\nor the  `priceFilter` is other that `FREE`, `PAID`, `ALL`;\nor `401 Unauthorized`, either because of wrong [client auth headers](#client-authentication-failure),\nor because the [Google Play Token Header](#google-play-token-header) is missing.\n\n\n\n### Collection endpoints\n\n\n\n#### Publish a shared collection\n\nThe `POST /collections` serves to publish a new shared collection, whose author is the client's user.\nThe request must include the [client authentication headers](#client-authentication-headers).\nThe body entity of the request should be an object with the information about the new collection.\nThis object is a reduced version of a [shared collection card](#shared-collection-card),\nrestricted to the fields  `author`, `name`, `installations`, `views`, `category`, `icon`,\n`community`, and `packages`, only. Of these, `views` and `installations` are optional fields.\n\nHere is an example of this body request:\n\n```json\n{\n  \"author\" : \"Jon Doe\",\n  \"name\" : \"Social Appathy\",\n  \"category\" : \"SOCIAL\",\n  \"icon\" : \"http://photo.blogs/url/of/icon\",\n  \"community\" : false,\n  \"installations\": 11,\n  \"views\": 13,\n  \"packages\" : [ \"one.valid.package\", \"non.existing.app\" ]\n}\n```\n\nIf successful, the response body is an object with two fields:\n* `publicIdentifier`: a string, with the public identifier of the new collection, as given to it by the backend.\n* `packageStats`: an object with a single field `added`, which is the number of packages added to the collection.\n\nThe response status of this endpoint can be `200 OK` if it was successful; or `401 Unauthorized`, if there is a\n[client authentication failure](#client-authentication-failure); or `400 BadRequest` if the request body is malformed.\n\n\n#### Read a collection\n\nThe endpoint `GET /collections/{collectionId}` gets all the information about a shared collection,\nwhose public identifier is passed in the `collectionId` segment.\nThe request must include the [client authentication headers](#client-authentication-headers)\nand the [Google Play Token Header](#google-play-token-header).\n\nIf successful, the response body is an object, whose fields are those of a\n[_shared collection_ card](#application-cards). Note that the `publicIdentifier` field\nin this card should be identical to the `collectionId` path segment.\nSee [an example of a response from this endpoint](#application-cards)\n\nThe response status can be  `200 OK` if the shared collection exists;\nor  `404 NotFound`, if there is no collection with the given `collectionId`;\nor `401 Unauthorized`, either because of wrong [client auth headers](#client-authentication-failure),\n or because the [Google Play Token Header](#google-play-token-header) is missing.\n\n\n#### Edit a collection\n\nThe `PUT /collections/{collectionId}` endpoint serves to edit the data of the collection\nwhose `publicIdentifier` is given by the `collectionId` path segment.\nThis can only be done if the collection was published by the client's user.\nThe request must include the [client authentication headers](#client-authentication-headers).\nThe body entity should be an object with two fields:\n* `collectionInfo` (optional field): an object with a single field `title`.\n* `packages`: a list of strings, each of which is a _package name_, which gives the new set of\n  applications contained in the collection.\n\nHere is an example of the request body, for a call to `PUT /collections/sociappathy`,\n\n```json\n{\n  \"collectionInfo\" : { \"title\" : \"More Social Appathy\"},\n  \"packages\" : [ \"com.twitter\", \"com.facebook\" ]\n}\n```\n\nIf successful, the response body is an object with two fields:\n* `publicIdentifier`: a string exactly equal to the `collectionId` segment.\n* `packageStats`: an object which counts the number of changes in the collection list of applications.\n  This object has up to two fields: `added`, an integer number that counts how many packages were added to the collection,\n  and the optional field `removed`, another integer number that counts how many packages were removed.\nHere is an example of the response body:\n\n```json\n{\n  \"publicIdentifier\" : \"sociappathy\" ,\n  \"packageStats\" : { \"added\" : 2, \"removed\" : 1 }\n}\n```\n\nThe response status can be\n* `200 OK` if the request could be performed.\n* `401 Unauthorized` if there is a [client authentication failure](#client-authentication-failure).\n* `403 Forbidden` if the client is authenticated, but is not the author of the collection.\n* `404 NotFound` if there is no collection whose public identifier is the `collectionId` given in a path param.\n\n\n#### Collections published by user\n\nThis endpoint, `GET /collections`, gives the list of collections published by the client user.\nThe request must include the [client authentication headers](#client-authentication-headers)\nand the [Google Play Token Header](#google-play-token-header).\nIf successful, the response entity is a [shared collection list](#shared-collection-list).\nThe response status can be one of the following:\n* `200 OK` if the request could be performed.\n* `401 Unauthorized` if there is a [client authentication failure](#client-authentication-failure).\n\n#### List of collections by category\n\nThe endpoints `GET /collections/{sort}/{category}/{pageNumber}/{pageSize}` are used to\nobtain a list of shared collections within a category, sorted according to a criterion of interest,\nand paginated. The endpoint takes the following path segments as parameters:\n* The `sort` parameter indicates the order in which to sort the collections. It can be one of the following:\n..* `latest`, to sort collections from most recently to least recently published.\n..* `top`, to sort collections from most downloaded to least downloaded.\n* The `category` parameter indicates the category in which we look for collections.\n* The `pageSize` is the number of elements in each page.\n* The `pageNumber` is the number of the page that is retrieved.\n\nThe request must include the [client authentication headers](#client-authentication-headers)\nand the [Google Play Token Header](#google-play-token-header).\nIf successful, the response body is a [shared collection list](#shared-collection-list).\nThe response status for this endpoint can be one of the following:\n* `200 OK` if the request is successful.\n* `401 Unauthorized` if the client authentication headers are wrong or missing.\n* `404 NotFound` if the `category` path segment corresponds to no category,\n    or if the `sort` path segment is other than `latest` or `top`.\n\n### Subscription endpoints\n\nThese endpoints are used to manage the subscription relation between the client and those collections\npublished by other users. All of these endpoints require the\n[client authentication headers](#client-authentication-headers) from the request,\nand fail with a `401 Unauthorized` [if they fail](#client-authorization-failure).\n\n#### List of subscribed collections\n\nThe `GET /collections/subscriptions` endpoint gives back a list of public collections\nthat the client's user is subscribed to.\nThe request must include the [client authentication headers](#client-authentication-headers).\nIf successful, the response is a list of strings, each of which is the public identifier of\none of the collections the user is subscribed to. \n\nAn example of a body response would be:\n\n```json\n[ \"one.pack.age\", \"two.pack.age\" ]\n```\n\nThe response status can be one of the following:\n* `200 OK` if the shared collection exists, in which case the user has been unsubscribed (if it was).\n* `401 Unauthorized` if there is a [client authentication failure](#client-authentication-failure).\n\n#### Subscribe to a shared collection\n\nThe `PUT /collections/subscriptions/{collectionId}` endpoint subscribes the client's user\nto the public shared collection whose public identifier is given in the `collectionId` path segment.\nThe request must include the [client authentication headers](#client-authentication-headers).\nThe endpoint takes no request entity body. If it succeeds, it produces an empty object `{}` in the response body.\nThe response status can be one of the following:\n* `200 OK` if the shared collection exists, in which case the user has been unsubscribed (if it was)\n* `401 Unauthorized` if any of the headers for client authentication is missing.\n* `404 NotFound` if there is no collection with the given `collectionId`.\n\n#### Unsubscribe from a shared collection\n\nThe  `DELETE /collections/subscriptions/{collectionId}` endpoint unsubscribes the sender\nfrom the public collection whose public identifier is given by the `collectionId` path segment.\nThe endpoint requires  the [client authentication headers](#client-authentication-headers).\nIf it succeeds, it produces an empty object `{}` in the response body.\nThe response status can be one of the following:\n* `200 OK` if the shared collection exists, in which case the user has been unsubscribed (if they were).\n* `401 Unauthorized` if there is a [client authentication failure](#client-authentication-failure).\n* `404 NotFound` if there isn't a collection with the given `collectionId`.\n\n\n\n### Rankings Endpoints\n\nThese endpoints allow for reading and refreshing the rankings that the backend collects\nfrom the Google Analytics API, irrespective of the apps in the client.\nThese are special endpoints, in that they're not used by a client, but by the\nmanagement of the backend. For this reason, they do _not_ require the\n[client authentication headers](#client-authentication-headers).\n\nWith these endpoints, we use a path segment to indicate the geographic scope of the ranking.\nThis geographic scope can be one of the following:\n* The whole world, which is represented as the path prefix `/world`.\n* A continent, which is expressed as the path segments `/continents/{cont}`, where `cont`\n  can be one of `Africa`, `Americas`, `Oceania`, `Europe` or `Asia`.\n* A country, which is expressed as the path segments `/countries/{count}`, where `count` is the name\n  of a country supported by the backend.\n  At present, we only support `Spain`, `United_Kingdom`, and `United_States`.\n\n\n#### Read a Ranking\n\nThe `GET /rankings/{geographic}` endpoints provide the full list of rankings for a geographic scope.\nThis endpoint doesn't need any special headers.\nIf successful, the response body entity is an object with a single field, `categories`,\nwhose value is a list of objects. Each of these objects is the ranking for a given category.\nIts fields are:\n* `category`: a string with the name of the category.\n* `apps`: a list of app package names, ordered by ranking (first position to the last position).\n* \nHere is an example of this response:\n\n```json\n{\n  \"categories\": [\n    {\n      \"category\" : \"SOCIAL\",\n      \"apps\" : [ \"com.facebook\", \"com.twitter\" ]\n    },\n    {\n      \"category\" : \"GAME_ARCADE\",\n      \"apps\" : [ \"old.games.doom]\n    }\n  ]\n}\n```\n\nThe response status can be `200 OK` if it succeeds; or `404 NotFound`, if the geographic scope\ndoes not correspond to one of those  [described above](#rankings-endpoints).\n\n#### Refresh a Ranking\nThe `POST /rankings/{geographic}` endpoints serve to refresh the rankings for the given geographic scope.\n\nThis endpoint requires a special `X-Google-Analytics-Token`, whose value carries the OAuth2 token\nthat grants the backend server access to the Google Analytics Report that collects the statistics\non 9 Cards users, from which the rankings are made.\n\nThe entity body in the request must be an object with the following three fields:\n* `startDate`: a string that gives the start day from which we start the analytics sample.\n  Syntactically, it has the format `\"yyyy-MM-dd\"`, which gives the number of the year, the number of the month,\n  and the number of the day of the month, for the desired date.\n* `endDate`: a string, much like the `startDate`, that gives the end day of the sample period.\n  Syntactically, it is like the `startDate` field.\n* `rankingLength`: an integer number which gives the number of applications that each category's ranking should have _at most_.\n  These applications, of course, would be the most valued.\n\nThe following is an example of this request:\n\n```json\n{\n  \"startDate\" : \"2016-01-31\",\n  \"endDate\" : \"2016-03-21\",\n  \"rankingLength\" : 10\n}\n```\n\nIf successful, the endpoint returns an empty object `{}`. If there is a failure in the\nexecution of the endpoint, it returns an object with these fields:\n* `error`: a number with the status code of the error.\n* `message`: a message with the error.\n* `status`: a string describing the reason for the error.\n\nThe response status can be `200 OK` if it succeeds, or `404 NotFound`, if the geographic scope\ndoes not correspond to one of those  [described above](#rankings-endpoints).\nThe response can be `400 BadRequest` if the dates in the request are wrong,\neither because one is a wrong date (such as `\"2016-02-30\"`),\nor because the `endDate` precedes the `startDate`,\nor because the `startDate` precedes the launch of Google Analytics."
  },
  {
    "path": "modules/docs/src/main/tut/docs/server/Installation.md",
    "content": "---\nlayout: docs\ntitle: Install Server\nsection: docs\n---\n\n## Installation\n\nIn this section, we outline the steps to setup your computer for developing and\nrunning the Nine-Cards-Back-End (NCBE) application.\n\n### Scala Development Tools\n\nThe NCBE is written in [Scala](www.scala-lang.org), using the [Scala Build Tool (SBT)](http://www.scala-sbt.org/) for an automatic build.\n\n*   Java SE 8 Development Kit: you can use [OpenJDK](http://openjdk.java.net/projects/jdk8/).\n    In Ubuntu/Debian, if you have several versions of the JDK installed, you may need to use the `update-java-alternatives` program.\n*   [Scala 2.11.8](http://www.scala-lang.org/download/2.11.8.html).\n*   [SBT version 0.13.8](http://www.scala-sbt.org/download.html) or later.\n\nIf you have an older version of SBT, or Scala, `sbt` can often bootstrap itself to newer versions.\n\nYou should ensure that the `PATH` environment variable contains the directories in which the programs `scala`, `scalac`, and `sbt` are located.\n\n### Postgres Database Setup\n\nThe NCBE stores its information in a database, using the [[postgresql]] database system.\nIt uses a database called `ninecards`. To write, run, and test NCBE in your machine, you can create\na user `ninecards_user`, with password `ninecards_pass`.\n\n#### Installation\n\nIn a Debian-based Linux distribution, you can use the `apt-get` command to install the packages:\n\n    sudo apt-get install postgresql postgresql-contrib postgresql-client pgadmin3\n\nFor OS-X users, you can use any of the tools mentioned [here](http://www.postgresql.org/download/macosx/).\n\n#### Setting Client authentication\n\nIn PostgreSQL, the \"Client Authentication\" method used for opening a client's session, can be set differently for each user, database, or connection.\nThis configuration is kept in a file called [`pg_hba.conf`](http://www.postgresql.org/docs/9.1/static/auth-pg-hba-conf.html).\n\n* In Debian-based distributions, it is located in the directory `/etc/postgresql/{version}/main/pg_hba.conf`.\n* In OS-X, you can find it using the command `locate pg_hba.conf`, or following [these instructions](http://stackoverflow.com/questions/14025972/postgresql-how-to-find-pg-hba-conf-file-using-mac-os-x).\n\nTo run and test the NCBE on our local host using the `ninecards_user` user, we need to open channels for the command line and for the JDBC driver.\n\n* The JDBC used by the NCBE enters the database through a local IPv4 connection. The following line should be in `pg_hba.conf` to allow this to happen:\n\n        host    ninecards       all  127.0.0.1/32            md5\n\n* For setting up the database for tests, we want to enter the database from a shell terminal, using the command `psql` as the `ninecards_user`. To allow this, you should have the following line in `pg_hba.conf`:\n\n        local   ninecards       ninecards_user                          md5\n\n* You need to restart the Postgres server for the changes to take effect. To do this, run the following command in a terminal:\n\n    \tsudo service postgresql restart\n\n#### Setting user and password for local development:\n\nTo create the `ninecards` database and the `ninecards_user` we need to open a session as the PostgreSQL-server administrator.\nThe administrator is the DBMS user called `postgres`, and by default it is configured to use  `peer` authentication.\nUnder this method, you can only open a DBMS session from a OS user with the same name.\nThus, you need to follow these steps:\n\n1. Start `psql`, the PostgreSQL command-line client, as the `postgres` OS user:\n\n    \tsudo -u postgres psql\n\n2. Inside `psql`, create the database, the user, the permissions, and exit:\n\n        create database ninecards ;\n        create user ninecards_user PASSWORD 'ninecards_pass';\n        GRANT ALL ON DATABASE ninecards TO ninecards_user;\n    \t\\q\n\n3. From your own OS user, you should now be able to open a postgres-client session using the following command:\n\n        psql --username=ninecards_user ninecards --password\n\n#### Database Schema Migrations\n\nThe evolutions for the data schema of the `ninecards` database are managed by `sbt`, the build system, using the [flyway SBT plugin](https://flywaydb.org/documentation/sbt/).\nFlyway needs a few configuration parameters to access the database.\nAn overview on how to pass these settings is given in the [Database Connection Configuration](#database-connection-configuration) section.\nSuffice it to say that, to run the migrations on your local database, you can use the configuration values written in the [`localhost.conf`](modules/api/src/main/resources/localhost.conf) file.\nYou can pass this file to `sbt`, by opening a shell session in the `nine-cards-backend` root directory and executing the following command:\n\n        sbt -Dconfig.file=\"modules/api/src/main/resources/localhost.conf\"\n\nThis should open an interactive `sbt` session. Inside this session,\nyou can clear the database with the command `flywayClean`, or perform the database migrations with `flywayMigrate`.\n\n**Note**: since `flyway` connects to the database through JDBC, you would need to configure the PostgreSQL authentication file `pg_hba.conf`, as explained [in a previous section](#setting-client-authentication).\n\n\n## Running and testing the application\n\nFrom a command line, within the root directory of the project, run the following:\n\n    $ sbt -Dconfig.file=\"modules/api/src/main/resources/localhost.conf\"\n    > project api\n    > run\n\nTo check that the application has started correctly, you can try accessing the Swagger apidocs in the\n`http://localhost:8080/apiDocs` URL.\n\n### Database Connection Configuration\n\nThe configuration is managed using Lightbend's [configuration library](https://github.com/typesafehub/config).\nThe default configuration is at the `modules/api/src/main/resources/application.conf` file, which\nloads the values for some configuration settings from the environment. This gives you several ways to\ndefine your configuration settings:\n\na. Run `sbt` passing the configuration settings, each setting having the form `-D{key}}={{value}}`.\n   For example, to run the application in your local host, you would pass the database configuration as follows:\n\n        sbt -Ddb.default.driver=\"org.postgresql.Driver\" -Ddb.default.url=\"jdbc:postgresql://localhost/ninecards\" -Ddb.default.user=\"ninecards\" -Ddb.default.password=\"ninecards_pass\"\n\nb. Write a configuration file with your settings, and pass that file to `sbt` using the `-Dconfig.file` option.\n    For example, to run the application in your local host, you can pass the [`localhost.conf` file](modules/api/src/main/resources/localhost.conf), as follows:\n\n        sbt -Dconfig.file=\"modules/api/src/main/resources/localhost.conf\"\n\nc. Set the shell environment variables used by the default configuration file.\n    In `bash`, this is done with the command `export VAR=[VALUE]`, without spaces.\n    For instance, to initialize the environment variables related to the database configuration, and set them for local execution, you would run the following:\n\n        export DB_DEFAULT_DRIVER=\"org.postgresql.Driver\"\n        export DB_DEFAULT_URL=\"jdbc:postgresql://localhost/ninecards\"\n        export DB_DEFAULT_USER=\"ninecards\"\n        export DB_DEFAULT_PASSWORD=\"ninecards_pass\"\n\n    Note that there should be no whitespace around the `=` sign. Note also that the settings only remain for the bash session.\n    You can write such settings in the `.bashrc` file, or in a executable shell script.\n\n### Testing and running the endpoints with Postman\n\nOnce the application is running and bound to the chosen port, you can run the endpoints by issuing HTTP\nrequests with any HTTP client, like [`curl`](https://en.wikipedia.org/wiki/CURL).\nIn particular, we use the [Postman](https://www.getpostman.com/) graphic client.\nPostman allows us to write a collection of HTTP requests and store it as a text file.\nThese requests can depend on variables read from an environment that is also stored as a text file.\n\nTo test the endpoints of the application, we provide a [collection](assets/postman/collection.json) of Postman requests,\nas well as an [environment](assets/postman/environment.json) for those requests.\n\n\n\n## Deployment - Preparing the Application\n\nThe NCBE is a server-side application, and it should be deployed as a\n[Infrastracture as a Service (IaaS)](https://en.wikipedia.org/wiki/Cloud_computing#Infrastructure_as_a_service_.28IaaS.29)\nor [Platform as a Service (PaaS)](https://en.wikipedia.org/wiki/Platform_as_a_service).\nTo do this, we need to pack the application's source code, the binary classes, its transitive dependencies,\nand the configuration values into a self-contained executable file (or *fat-JAR*).\nThis is done with the [`sbt-assembly` plugin](https://github.com/sbt/sbt-assembly).\nThis plugin was originally ported from codahale's assembly-sbt and may have been inspired by Maven's assembly plugin.\nIts goal is to build a fat JAR of your project with all of its dependencies.\n\nTo execute the plugin, you should open a shell session at the project's root directory and run the following command:\n\n    $ sbt \"project api\" assembly\n\nNote that you should provide the database configuration variables to the `sbt` command, using any of the methods described above.\nOtherwise, the `sbt` fails due to the `flyway` plugin.\nBy default, the fat jar will be created in the `{appPath}/modules/api/target/scala-2.11/` folder.\n\n### Running SQL evolutions in Heroku\n\nThis task should be done manually, as follows:\n\n    $ heroku pg:psql --app nine-cards < /path/to/file.sql"
  },
  {
    "path": "modules/docs/src/main/tut/index.md",
    "content": "---\nlayout: ninecards-home\npermalink: /\n---\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/accounts/Account.java",
    "content": "package android.accounts;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\npublic class Account implements Parcelable {\n    public String name;\n    public String type;\n    public static Creator<Account> CREATOR = null;\n\n    public Account(String name, String type) {\n        this.name = name;\n        this.type = type;\n    }\n\n    public Account(Parcel in) {}\n\n    public boolean equals(Object o) { return true; }\n\n    public int hashCode() { return 0;}\n\n    public int describeContents() { return 0;}\n\n    public void writeToParcel(Parcel dest, int flags) {}\n\n    public String toString() { return \"\";}\n}"
  },
  {
    "path": "modules/mock-android/src/main/java/android/accounts/AccountManager.java",
    "content": "package android.accounts;\n\nimport android.accounts.Account;\nimport android.accounts.AccountManagerCallback;\nimport android.accounts.AccountManagerFuture;\nimport android.accounts.AuthenticatorDescription;\nimport android.accounts.AuthenticatorException;\nimport android.accounts.OnAccountsUpdateListener;\nimport android.accounts.OperationCanceledException;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class AccountManager {\n    public static final String ACTION_AUTHENTICATOR_INTENT = \"android.accounts.AccountAuthenticator\";\n    public static final String AUTHENTICATOR_ATTRIBUTES_NAME = \"account-authenticator\";\n    public static final String AUTHENTICATOR_META_DATA_NAME = \"android.accounts.AccountAuthenticator\";\n    public static final int ERROR_CODE_BAD_ARGUMENTS = 7;\n    public static final int ERROR_CODE_BAD_AUTHENTICATION = 9;\n    public static final int ERROR_CODE_BAD_REQUEST = 8;\n    public static final int ERROR_CODE_CANCELED = 4;\n    public static final int ERROR_CODE_INVALID_RESPONSE = 5;\n    public static final int ERROR_CODE_NETWORK_ERROR = 3;\n    public static final int ERROR_CODE_REMOTE_EXCEPTION = 1;\n    public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6;\n    public static final String KEY_ACCOUNTS = \"accounts\";\n    public static final String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = \"accountAuthenticatorResponse\";\n    public static final String KEY_ACCOUNT_MANAGER_RESPONSE = \"accountManagerResponse\";\n    public static final String KEY_ACCOUNT_NAME = \"authAccount\";\n    public static final String KEY_ACCOUNT_TYPE = \"accountType\";\n    public static final String KEY_ANDROID_PACKAGE_NAME = \"androidPackageName\";\n    public static final String KEY_AUTHENTICATOR_TYPES = \"authenticator_types\";\n    public static final String KEY_AUTHTOKEN = \"authtoken\";\n    public static final String KEY_AUTH_FAILED_MESSAGE = \"authFailedMessage\";\n    public static final String KEY_AUTH_TOKEN_LABEL = \"authTokenLabelKey\";\n    public static final String KEY_BOOLEAN_RESULT = \"booleanResult\";\n    public static final String KEY_CALLER_PID = \"callerPid\";\n    public static final String KEY_CALLER_UID = \"callerUid\";\n    public static final String KEY_ERROR_CODE = \"errorCode\";\n    public static final String KEY_ERROR_MESSAGE = \"errorMessage\";\n    public static final String KEY_INTENT = \"intent\";\n    public static final String KEY_LAST_AUTHENTICATED_TIME = \"lastAuthenticatedTime\";\n    public static final String KEY_PASSWORD = \"password\";\n    public static final String KEY_USERDATA = \"userdata\";\n    public static final String LOGIN_ACCOUNTS_CHANGED_ACTION = \"android.accounts.LOGIN_ACCOUNTS_CHANGED\";\n\n    AccountManager() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static AccountManager get(Context context) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public String getPassword(Account account) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public String getUserData(Account account, String key) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public AuthenticatorDescription[] getAuthenticatorTypes() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public Account[] getAccounts() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public Account[] getAccountsByTypeForPackage(String type, String packageName) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public Account[] getAccountsByType(String type) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public AccountManagerFuture<Boolean> hasFeatures(Account account, String[] features, AccountManagerCallback<Boolean> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(String type, String[] features, AccountManagerCallback<Account[]> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean notifyAccountAuthenticated(Account account) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public AccountManagerFuture<Account> renameAccount(Account account, String newName, AccountManagerCallback<Account> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public String getPreviousName(Account account) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    /** @deprecated */\n    @Deprecated\n    public AccountManagerFuture<Boolean> removeAccount(Account account, AccountManagerCallback<Boolean> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public AccountManagerFuture<Bundle> removeAccount(Account account, Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean removeAccountExplicitly(Account account) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void invalidateAuthToken(String accountType, String authToken) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public String peekAuthToken(Account account, String authTokenType) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void setPassword(Account account, String password) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void clearPassword(Account account) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void setUserData(Account account, String key, String value) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void setAuthToken(Account account, String authTokenType, String authToken) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public String blockingGetAuthToken(Account account, String authTokenType, boolean notifyAuthFailure) throws OperationCanceledException, IOException, AuthenticatorException {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public AccountManagerFuture<Bundle> getAuthToken(Account account, String authTokenType, Bundle options, Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    /** @deprecated */\n    @Deprecated\n    public AccountManagerFuture<Bundle> getAuthToken(Account account, String authTokenType, boolean notifyAuthFailure, AccountManagerCallback<Bundle> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public AccountManagerFuture<Bundle> getAuthToken(Account account, String authTokenType, Bundle options, boolean notifyAuthFailure, AccountManagerCallback<Bundle> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public AccountManagerFuture<Bundle> addAccount(String accountType, String authTokenType, String[] requiredFeatures, Bundle addAccountOptions, Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public AccountManagerFuture<Bundle> confirmCredentials(Account account, Bundle options, Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public AccountManagerFuture<Bundle> updateCredentials(Account account, String authTokenType, Bundle options, Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public AccountManagerFuture<Bundle> editProperties(String accountType, Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public AccountManagerFuture<Bundle> getAuthTokenByFeatures(String accountType, String authTokenType, String[] features, Activity activity, Bundle addAccountOptions, Bundle getAuthTokenOptions, AccountManagerCallback<Bundle> callback, Handler handler) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    /** @deprecated */\n    @Deprecated\n    public static Intent newChooseAccountIntent(Account selectedAccount, ArrayList<Account> allowableAccounts, String[] allowableAccountTypes, boolean alwaysPromptForAccount, String descriptionOverrideText, String addAccountAuthTokenType, String[] addAccountRequiredFeatures, Bundle addAccountOptions) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Intent newChooseAccountIntent(Account selectedAccount, List<Account> allowableAccounts, String[] allowableAccountTypes, String descriptionOverrideText, String addAccountAuthTokenType, String[] addAccountRequiredFeatures, Bundle addAccountOptions) {\n        return null;\n    }\n\n    public void addOnAccountsUpdatedListener(OnAccountsUpdateListener listener, Handler handler, boolean updateImmediately) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void removeOnAccountsUpdatedListener(OnAccountsUpdateListener listener) {\n        throw new RuntimeException(\"Stub!\");\n    }\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/accounts/AccountsException.java",
    "content": "package android.accounts;\n\npublic class AccountsException extends Exception {\n    public AccountsException() {\n        super();\n    }\n    public AccountsException(String message) {\n        super(message);\n    }\n    public AccountsException(String message, Throwable cause) {\n        super(message, cause);\n    }\n    public AccountsException(Throwable cause) {\n        super(cause);\n    }\n}"
  },
  {
    "path": "modules/mock-android/src/main/java/android/accounts/OperationCanceledException.java",
    "content": "package android.accounts;\n\npublic class OperationCanceledException extends AccountsException {\n    public OperationCanceledException() {\n        super();\n    }\n    public OperationCanceledException(String message) {\n        super(message);\n    }\n    public OperationCanceledException(String message, Throwable cause) {\n        super(message, cause);\n    }\n    public OperationCanceledException(Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/app/Activity.java",
    "content": "package android.app;\n\nimport android.view.ActionMode;\nimport android.view.SearchEvent;\n\npublic class Activity\n  extends android.view.ContextThemeWrapper\n  implements android.view.LayoutInflater.Factory2, android.view.Window.Callback, android.view.KeyEvent.Callback, android.view.View.OnCreateContextMenuListener, android.content.ComponentCallbacks2\n{\npublic  Activity() { throw new RuntimeException(\"Stub!\"); }\npublic  android.content.Intent getIntent() { throw new RuntimeException(\"Stub!\"); }\npublic  void setIntent(android.content.Intent newIntent) { throw new RuntimeException(\"Stub!\"); }\npublic final  android.app.Application getApplication() { throw new RuntimeException(\"Stub!\"); }\npublic final  boolean isChild() { throw new RuntimeException(\"Stub!\"); }\npublic final  android.app.Activity getParent() { throw new RuntimeException(\"Stub!\"); }\npublic  android.view.WindowManager getWindowManager() { throw new RuntimeException(\"Stub!\"); }\npublic  android.view.Window getWindow() { throw new RuntimeException(\"Stub!\"); }\npublic  android.app.LoaderManager getLoaderManager() { throw new RuntimeException(\"Stub!\"); }\npublic  android.view.View getCurrentFocus() { throw new RuntimeException(\"Stub!\"); }\nprotected  void onCreate(android.os.Bundle savedInstanceState) { throw new RuntimeException(\"Stub!\"); }\nprotected  void onRestoreInstanceState(android.os.Bundle savedInstanceState) { throw new RuntimeException(\"Stub!\"); }\nprotected  void onPostCreate(android.os.Bundle savedInstanceState) { throw new RuntimeException(\"Stub!\"); }\nprotected  void onStart() { throw new RuntimeException(\"Stub!\"); }\nprotected  void onRestart() { throw new RuntimeException(\"Stub!\"); }\nprotected  void onResume() { throw new RuntimeException(\"Stub!\"); }\nprotected  void onPostResume() { throw new RuntimeException(\"Stub!\"); }\nprotected  void onNewIntent(android.content.Intent intent) { throw new RuntimeException(\"Stub!\"); }\nprotected  void onSaveInstanceState(android.os.Bundle outState) { throw new RuntimeException(\"Stub!\"); }\nprotected  void onPause() { throw new RuntimeException(\"Stub!\"); }\nprotected  void onUserLeaveHint() { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onCreateThumbnail(android.graphics.Bitmap outBitmap, android.graphics.Canvas canvas) { throw new RuntimeException(\"Stub!\"); }\npublic  java.lang.CharSequence onCreateDescription() { throw new RuntimeException(\"Stub!\"); }\nprotected  void onStop() { throw new RuntimeException(\"Stub!\"); }\nprotected  void onDestroy() { throw new RuntimeException(\"Stub!\"); }\npublic  void onConfigurationChanged(android.content.res.Configuration newConfig) { throw new RuntimeException(\"Stub!\"); }\npublic  int getChangingConfigurations() { throw new RuntimeException(\"Stub!\"); }\n@java.lang.Deprecated()\npublic  java.lang.Object getLastNonConfigurationInstance() { throw new RuntimeException(\"Stub!\"); }\n@Deprecated\npublic  java.lang.Object onRetainNonConfigurationInstance() { throw new RuntimeException(\"Stub!\"); }\npublic  void onLowMemory() { throw new RuntimeException(\"Stub!\"); }\npublic  void onTrimMemory(int level) { throw new RuntimeException(\"Stub!\"); }\npublic  android.app.FragmentManager getFragmentManager() { throw new RuntimeException(\"Stub!\"); }\npublic  void onAttachFragment(android.app.Fragment fragment) { throw new RuntimeException(\"Stub!\"); }\n@java.lang.Deprecated()\npublic final  android.database.Cursor managedQuery(android.net.Uri uri, java.lang.String[] projection, java.lang.String selection, java.lang.String[] selectionArgs, java.lang.String sortOrder) { throw new RuntimeException(\"Stub!\"); }\n@java.lang.Deprecated()\npublic  void startManagingCursor(android.database.Cursor c) { throw new RuntimeException(\"Stub!\"); }\n@java.lang.Deprecated()\npublic  void stopManagingCursor(android.database.Cursor c) { throw new RuntimeException(\"Stub!\"); }\npublic  android.view.View findViewById(int id) { throw new RuntimeException(\"Stub!\"); }\npublic  android.app.ActionBar getActionBar() { throw new RuntimeException(\"Stub!\"); }\npublic  void setContentView(int layoutResID) { throw new RuntimeException(\"Stub!\"); }\npublic  void setContentView(android.view.View view) { throw new RuntimeException(\"Stub!\"); }\npublic  void setContentView(android.view.View view, android.view.ViewGroup.LayoutParams params) { throw new RuntimeException(\"Stub!\"); }\npublic  void addContentView(android.view.View view, android.view.ViewGroup.LayoutParams params) { throw new RuntimeException(\"Stub!\"); }\npublic  void setFinishOnTouchOutside(boolean finish) { throw new RuntimeException(\"Stub!\"); }\npublic final  void setDefaultKeyMode(int mode) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onKeyDown(int keyCode, android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onKeyLongPress(int keyCode, android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onKeyUp(int keyCode, android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onKeyMultiple(int keyCode, int repeatCount, android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\npublic  void onBackPressed() { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onKeyShortcut(int keyCode, android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onTouchEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onTrackballEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onGenericMotionEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\npublic  void onUserInteraction() { throw new RuntimeException(\"Stub!\"); }\npublic  void onWindowAttributesChanged(android.view.WindowManager.LayoutParams params) { throw new RuntimeException(\"Stub!\"); }\npublic  void onContentChanged() { throw new RuntimeException(\"Stub!\"); }\npublic  void onWindowFocusChanged(boolean hasFocus) { throw new RuntimeException(\"Stub!\"); }\npublic  void onAttachedToWindow() { throw new RuntimeException(\"Stub!\"); }\npublic  void onDetachedFromWindow() { throw new RuntimeException(\"Stub!\"); }\npublic  boolean hasWindowFocus() { throw new RuntimeException(\"Stub!\"); }\npublic  boolean dispatchKeyEvent(android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean dispatchKeyShortcutEvent(android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean dispatchTouchEvent(android.view.MotionEvent ev) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean dispatchTrackballEvent(android.view.MotionEvent ev) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean dispatchGenericMotionEvent(android.view.MotionEvent ev) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent event) { throw new RuntimeException(\"Stub!\"); }\npublic  android.view.View onCreatePanelView(int featureId) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onCreatePanelMenu(int featureId, android.view.Menu menu) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onPreparePanel(int featureId, android.view.View view, android.view.Menu menu) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onMenuOpened(int featureId, android.view.Menu menu) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onMenuItemSelected(int featureId, android.view.MenuItem item) { throw new RuntimeException(\"Stub!\"); }\npublic  void onPanelClosed(int featureId, android.view.Menu menu) { throw new RuntimeException(\"Stub!\"); }\npublic  void invalidateOptionsMenu() { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onCreateOptionsMenu(android.view.Menu menu) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onPrepareOptionsMenu(android.view.Menu menu) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onOptionsItemSelected(android.view.MenuItem item) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onNavigateUp() { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onNavigateUpFromChild(android.app.Activity child) { throw new RuntimeException(\"Stub!\"); }\npublic  void onCreateNavigateUpTaskStack(android.app.TaskStackBuilder builder) { throw new RuntimeException(\"Stub!\"); }\npublic  void onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder builder) { throw new RuntimeException(\"Stub!\"); }\npublic  void onOptionsMenuClosed(android.view.Menu menu) { throw new RuntimeException(\"Stub!\"); }\npublic  void openOptionsMenu() { throw new RuntimeException(\"Stub!\"); }\npublic  void closeOptionsMenu() { throw new RuntimeException(\"Stub!\"); }\npublic  void onCreateContextMenu(android.view.ContextMenu menu, android.view.View v, android.view.ContextMenu.ContextMenuInfo menuInfo) { throw new RuntimeException(\"Stub!\"); }\npublic  void registerForContextMenu(android.view.View view) { throw new RuntimeException(\"Stub!\"); }\npublic  void unregisterForContextMenu(android.view.View view) { throw new RuntimeException(\"Stub!\"); }\npublic  void openContextMenu(android.view.View view) { throw new RuntimeException(\"Stub!\"); }\npublic  void closeContextMenu() { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onContextItemSelected(android.view.MenuItem item) { throw new RuntimeException(\"Stub!\"); }\npublic  void onContextMenuClosed(android.view.Menu menu) { throw new RuntimeException(\"Stub!\"); }\n@java.lang.Deprecated()\nprotected  android.app.Dialog onCreateDialog(int id) { throw new RuntimeException(\"Stub!\"); }\n@java.lang.Deprecated()\nprotected  android.app.Dialog onCreateDialog(int id, android.os.Bundle args) { throw new RuntimeException(\"Stub!\"); }\n@java.lang.Deprecated()\nprotected  void onPrepareDialog(int id, android.app.Dialog dialog) { throw new RuntimeException(\"Stub!\"); }\n@java.lang.Deprecated()\nprotected  void onPrepareDialog(int id, android.app.Dialog dialog, android.os.Bundle args) { throw new RuntimeException(\"Stub!\"); }\n@java.lang.Deprecated()\npublic final  void showDialog(int id) { throw new RuntimeException(\"Stub!\"); }\n@java.lang.Deprecated()\npublic final  boolean showDialog(int id, android.os.Bundle args) { throw new RuntimeException(\"Stub!\"); }\n@java.lang.Deprecated()\npublic final  void dismissDialog(int id) { throw new RuntimeException(\"Stub!\"); }\n@java.lang.Deprecated()\npublic final  void removeDialog(int id) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean onSearchRequested() { throw new RuntimeException(\"Stub!\"); }\npublic boolean onSearchRequested(SearchEvent searchEvent) {throw new RuntimeException(\"Stub!\");}\npublic  void startSearch(java.lang.String initialQuery, boolean selectInitialQuery, android.os.Bundle appSearchData, boolean globalSearch) { throw new RuntimeException(\"Stub!\"); }\npublic  void triggerSearch(java.lang.String query, android.os.Bundle appSearchData) { throw new RuntimeException(\"Stub!\"); }\npublic  void takeKeyEvents(boolean get) { throw new RuntimeException(\"Stub!\"); }\npublic final  boolean requestWindowFeature(int featureId) { throw new RuntimeException(\"Stub!\"); }\npublic final  void setFeatureDrawableResource(int featureId, int resId) { throw new RuntimeException(\"Stub!\"); }\npublic final  void setFeatureDrawableUri(int featureId, android.net.Uri uri) { throw new RuntimeException(\"Stub!\"); }\npublic final  void setFeatureDrawable(int featureId, android.graphics.drawable.Drawable drawable) { throw new RuntimeException(\"Stub!\"); }\npublic final  void setFeatureDrawableAlpha(int featureId, int alpha) { throw new RuntimeException(\"Stub!\"); }\npublic  android.view.LayoutInflater getLayoutInflater() { throw new RuntimeException(\"Stub!\"); }\npublic  android.view.MenuInflater getMenuInflater() { throw new RuntimeException(\"Stub!\"); }\nprotected  void onApplyThemeResource(android.content.res.Resources.Theme theme, int resid, boolean first) { throw new RuntimeException(\"Stub!\"); }\npublic  void startActivityForResult(android.content.Intent intent, int requestCode) { throw new RuntimeException(\"Stub!\"); }\npublic  void startActivityForResult(android.content.Intent intent, int requestCode, android.os.Bundle options) { throw new RuntimeException(\"Stub!\"); }\npublic  void startIntentSenderForResult(android.content.IntentSender intent, int requestCode, android.content.Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws android.content.IntentSender.SendIntentException { throw new RuntimeException(\"Stub!\"); }\npublic  void startIntentSenderForResult(android.content.IntentSender intent, int requestCode, android.content.Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, android.os.Bundle options) throws android.content.IntentSender.SendIntentException { throw new RuntimeException(\"Stub!\"); }\npublic  void startActivity(android.content.Intent intent) { throw new RuntimeException(\"Stub!\"); }\npublic  void startActivity(android.content.Intent intent, android.os.Bundle options) { throw new RuntimeException(\"Stub!\"); }\npublic  void startActivities(android.content.Intent[] intents) { throw new RuntimeException(\"Stub!\"); }\npublic  void startActivities(android.content.Intent[] intents, android.os.Bundle options) { throw new RuntimeException(\"Stub!\"); }\npublic  void startIntentSender(android.content.IntentSender intent, android.content.Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws android.content.IntentSender.SendIntentException { throw new RuntimeException(\"Stub!\"); }\npublic  void startIntentSender(android.content.IntentSender intent, android.content.Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, android.os.Bundle options) throws android.content.IntentSender.SendIntentException { throw new RuntimeException(\"Stub!\"); }\npublic  boolean startActivityIfNeeded(android.content.Intent intent, int requestCode) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean startActivityIfNeeded(android.content.Intent intent, int requestCode, android.os.Bundle options) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean startNextMatchingActivity(android.content.Intent intent) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean startNextMatchingActivity(android.content.Intent intent, android.os.Bundle options) { throw new RuntimeException(\"Stub!\"); }\npublic  void startActivityFromChild(android.app.Activity child, android.content.Intent intent, int requestCode) { throw new RuntimeException(\"Stub!\"); }\npublic  void startActivityFromChild(android.app.Activity child, android.content.Intent intent, int requestCode, android.os.Bundle options) { throw new RuntimeException(\"Stub!\"); }\npublic  void startActivityFromFragment(android.app.Fragment fragment, android.content.Intent intent, int requestCode) { throw new RuntimeException(\"Stub!\"); }\npublic  void startActivityFromFragment(android.app.Fragment fragment, android.content.Intent intent, int requestCode, android.os.Bundle options) { throw new RuntimeException(\"Stub!\"); }\npublic  void startIntentSenderFromChild(android.app.Activity child, android.content.IntentSender intent, int requestCode, android.content.Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws android.content.IntentSender.SendIntentException { throw new RuntimeException(\"Stub!\"); }\npublic  void startIntentSenderFromChild(android.app.Activity child, android.content.IntentSender intent, int requestCode, android.content.Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, android.os.Bundle options) throws android.content.IntentSender.SendIntentException { throw new RuntimeException(\"Stub!\"); }\npublic  void overridePendingTransition(int enterAnim, int exitAnim) { throw new RuntimeException(\"Stub!\"); }\npublic  void setResult(int resultCode) {  }\npublic  void setResult(int resultCode, android.content.Intent data) {  }\npublic  java.lang.String getCallingPackage() { throw new RuntimeException(\"Stub!\"); }\npublic  android.content.ComponentName getCallingActivity() { throw new RuntimeException(\"Stub!\"); }\npublic  void setVisible(boolean visible) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean isFinishing() { throw new RuntimeException(\"Stub!\"); }\npublic  boolean isChangingConfigurations() { throw new RuntimeException(\"Stub!\"); }\npublic  void recreate() { throw new RuntimeException(\"Stub!\"); }\npublic  void finish() {  }\npublic  void finishAffinity() { throw new RuntimeException(\"Stub!\"); }\npublic  void finishFromChild(android.app.Activity child) { throw new RuntimeException(\"Stub!\"); }\npublic  void finishActivity(int requestCode) { throw new RuntimeException(\"Stub!\"); }\npublic  void finishActivityFromChild(android.app.Activity child, int requestCode) { throw new RuntimeException(\"Stub!\"); }\nprotected  void onActivityResult(int requestCode, int resultCode, android.content.Intent data) { throw new RuntimeException(\"Stub!\"); }\npublic  android.app.PendingIntent createPendingResult(int requestCode, android.content.Intent data, int flags) { throw new RuntimeException(\"Stub!\"); }\npublic  void setRequestedOrientation(int requestedOrientation) { throw new RuntimeException(\"Stub!\"); }\npublic  int getRequestedOrientation() { throw new RuntimeException(\"Stub!\"); }\npublic  int getTaskId() { throw new RuntimeException(\"Stub!\"); }\npublic  boolean isTaskRoot() { throw new RuntimeException(\"Stub!\"); }\npublic  boolean moveTaskToBack(boolean nonRoot) { throw new RuntimeException(\"Stub!\"); }\npublic  java.lang.String getLocalClassName() { throw new RuntimeException(\"Stub!\"); }\npublic  android.content.ComponentName getComponentName() { throw new RuntimeException(\"Stub!\"); }\npublic  android.content.SharedPreferences getPreferences(int mode) { throw new RuntimeException(\"Stub!\"); }\npublic  java.lang.Object getSystemService(java.lang.String name) { throw new RuntimeException(\"Stub!\"); }\npublic  void setTitle(java.lang.CharSequence title) { throw new RuntimeException(\"Stub!\"); }\npublic  void setTitle(int titleId) { throw new RuntimeException(\"Stub!\"); }\npublic  void setTitleColor(int textColor) { throw new RuntimeException(\"Stub!\"); }\npublic final  java.lang.CharSequence getTitle() { throw new RuntimeException(\"Stub!\"); }\npublic final  int getTitleColor() { throw new RuntimeException(\"Stub!\"); }\nprotected  void onTitleChanged(java.lang.CharSequence title, int color) { throw new RuntimeException(\"Stub!\"); }\nprotected  void onChildTitleChanged(android.app.Activity childActivity, java.lang.CharSequence title) { throw new RuntimeException(\"Stub!\"); }\npublic final  void setProgressBarVisibility(boolean visible) { throw new RuntimeException(\"Stub!\"); }\npublic final  void setProgressBarIndeterminateVisibility(boolean visible) { throw new RuntimeException(\"Stub!\"); }\npublic final  void setProgressBarIndeterminate(boolean indeterminate) { throw new RuntimeException(\"Stub!\"); }\npublic final  void setProgress(int progress) { throw new RuntimeException(\"Stub!\"); }\npublic final  void setSecondaryProgress(int secondaryProgress) { throw new RuntimeException(\"Stub!\"); }\npublic final  void setVolumeControlStream(int streamType) { throw new RuntimeException(\"Stub!\"); }\npublic final  int getVolumeControlStream() { throw new RuntimeException(\"Stub!\"); }\npublic final  void runOnUiThread(java.lang.Runnable action) { throw new RuntimeException(\"Stub!\"); }\npublic  android.view.View onCreateView(java.lang.String name, android.content.Context context, android.util.AttributeSet attrs) { throw new RuntimeException(\"Stub!\"); }\npublic  android.view.View onCreateView(android.view.View parent, java.lang.String name, android.content.Context context, android.util.AttributeSet attrs) { throw new RuntimeException(\"Stub!\"); }\npublic  void dump(java.lang.String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, java.lang.String[] args) { throw new RuntimeException(\"Stub!\"); }\npublic  android.view.ActionMode startActionMode(android.view.ActionMode.Callback callback) { throw new RuntimeException(\"Stub!\"); }\npublic  android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback callback) { throw new RuntimeException(\"Stub!\"); }\npublic ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) {\n        return null;\n    }\npublic  void onActionModeStarted(android.view.ActionMode mode) { throw new RuntimeException(\"Stub!\"); }\npublic  void onActionModeFinished(android.view.ActionMode mode) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean shouldUpRecreateTask(android.content.Intent targetIntent) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean navigateUpTo(android.content.Intent upIntent) { throw new RuntimeException(\"Stub!\"); }\npublic  boolean navigateUpToFromChild(android.app.Activity child, android.content.Intent upIntent) { throw new RuntimeException(\"Stub!\"); }\npublic  android.content.Intent getParentActivityIntent() { throw new RuntimeException(\"Stub!\"); }\npublic static final int RESULT_CANCELED = 0;\npublic static final int RESULT_OK = -1;\npublic static final int RESULT_FIRST_USER = 1;\nprotected static final int[] FOCUSED_STATE_SET = null;\npublic static final int DEFAULT_KEYS_DISABLE = 0;\npublic static final int DEFAULT_KEYS_DIALER = 1;\npublic static final int DEFAULT_KEYS_SHORTCUT = 2;\npublic static final int DEFAULT_KEYS_SEARCH_LOCAL = 3;\npublic static final int DEFAULT_KEYS_SEARCH_GLOBAL = 4;\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/content/ComponentName.java",
    "content": "package android.content;\n\n\npublic class ComponentName {\n    private final String mPackage;\n    private final String mClass;\n\n    public ComponentName(String pkg, String cls) {\n        if (pkg == null) throw new NullPointerException(\"package name is null\");\n        if (cls == null) throw new NullPointerException(\"class name is null\");\n        mPackage = pkg;\n        mClass = cls;\n    }\n\n    public ComponentName(Context pkg, String cls) {\n        if (cls == null) throw new NullPointerException(\"class name is null\");\n        mPackage = pkg.getPackageName();\n        mClass = cls;\n    }\n\n    public ComponentName(Context pkg, Class<?> cls) {\n        mPackage = pkg.getPackageName();\n        mClass = cls.getName();\n    }\n\n    public String getPackageName() {\n        return mPackage;\n    }\n\n    public String getClassName() {\n        return mClass;\n    }\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/content/ContentResolver.java",
    "content": "package android.content;\npublic abstract class ContentResolver\n{\n    public  ContentResolver(android.content.Context context) { throw new RuntimeException(\"Stub!\"); }\n    public   java.lang.String getType(android.net.Uri url) { throw new RuntimeException(\"Stub!\"); }\n    public  java.lang.String[] getStreamTypes(android.net.Uri url, java.lang.String mimeTypeFilter) { throw new RuntimeException(\"Stub!\"); }\n    public   android.database.Cursor query(android.net.Uri uri, java.lang.String[] projection, java.lang.String selection, java.lang.String[] selectionArgs, java.lang.String sortOrder) { throw new RuntimeException(\"Stub!\"); }\n    public   android.database.Cursor query(android.net.Uri uri, java.lang.String[] projection, java.lang.String selection, java.lang.String[] selectionArgs, java.lang.String sortOrder, android.os.CancellationSignal cancellationSignal) { throw new RuntimeException(\"Stub!\"); }\n    public   java.io.InputStream openInputStream(android.net.Uri uri) throws java.io.FileNotFoundException { throw new RuntimeException(\"Stub!\"); }\n    public   java.io.OutputStream openOutputStream(android.net.Uri uri) throws java.io.FileNotFoundException { throw new RuntimeException(\"Stub!\"); }\n    public   java.io.OutputStream openOutputStream(android.net.Uri uri, java.lang.String mode) throws java.io.FileNotFoundException { throw new RuntimeException(\"Stub!\"); }\n    public   android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri uri, java.lang.String mode) throws java.io.FileNotFoundException { throw new RuntimeException(\"Stub!\"); }\n    public   android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri uri, java.lang.String mode) throws java.io.FileNotFoundException { throw new RuntimeException(\"Stub!\"); }\n    public   android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri uri, java.lang.String mimeType, android.os.Bundle opts) throws java.io.FileNotFoundException { throw new RuntimeException(\"Stub!\"); }\n    public   android.net.Uri insert(android.net.Uri url, android.content.ContentValues values) { throw new RuntimeException(\"Stub!\"); }\n    public  android.content.ContentProviderResult[] applyBatch(java.lang.String authority, java.util.ArrayList<android.content.ContentProviderOperation> operations) throws android.os.RemoteException, android.content.OperationApplicationException { throw new RuntimeException(\"Stub!\"); }\n    public   int bulkInsert(android.net.Uri url, android.content.ContentValues[] values) { throw new RuntimeException(\"Stub!\"); }\n    public   int delete(android.net.Uri url, java.lang.String where, java.lang.String[] selectionArgs) { throw new RuntimeException(\"Stub!\"); }\n    public   int update(android.net.Uri uri, android.content.ContentValues values, java.lang.String where, java.lang.String[] selectionArgs) { throw new RuntimeException(\"Stub!\"); }\n    public   android.os.Bundle call(android.net.Uri uri, java.lang.String method, java.lang.String arg, android.os.Bundle extras) { throw new RuntimeException(\"Stub!\"); }\n    public   android.content.ContentProviderClient acquireContentProviderClient(android.net.Uri uri) { throw new RuntimeException(\"Stub!\"); }\n    public   android.content.ContentProviderClient acquireContentProviderClient(java.lang.String name) { throw new RuntimeException(\"Stub!\"); }\n    public   android.content.ContentProviderClient acquireUnstableContentProviderClient(android.net.Uri uri) { throw new RuntimeException(\"Stub!\"); }\n    public   android.content.ContentProviderClient acquireUnstableContentProviderClient(java.lang.String name) { throw new RuntimeException(\"Stub!\"); }\n    public   void registerContentObserver(android.net.Uri uri, boolean notifyForDescendents, android.database.ContentObserver observer) {  }\n    public   void unregisterContentObserver(android.database.ContentObserver observer) {  }\n    public  void notifyChange(android.net.Uri uri, android.database.ContentObserver observer) { throw new RuntimeException(\"Stub!\"); }\n    public  void notifyChange(android.net.Uri uri, android.database.ContentObserver observer, boolean syncToNetwork) { throw new RuntimeException(\"Stub!\"); }\n    @java.lang.Deprecated()\n    public  void startSync(android.net.Uri uri, android.os.Bundle extras) { throw new RuntimeException(\"Stub!\"); }\n    public static  void requestSync(android.accounts.Account account, java.lang.String authority, android.os.Bundle extras) { throw new RuntimeException(\"Stub!\"); }\n    public static  void validateSyncExtrasBundle(android.os.Bundle extras) { throw new RuntimeException(\"Stub!\"); }\n    @java.lang.Deprecated()\n    public  void cancelSync(android.net.Uri uri) { throw new RuntimeException(\"Stub!\"); }\n    public static  void cancelSync(android.accounts.Account account, java.lang.String authority) { throw new RuntimeException(\"Stub!\"); }\n    public static  android.content.SyncAdapterType[] getSyncAdapterTypes() { throw new RuntimeException(\"Stub!\"); }\n    public static  boolean getSyncAutomatically(android.accounts.Account account, java.lang.String authority) { throw new RuntimeException(\"Stub!\"); }\n    public static  void setSyncAutomatically(android.accounts.Account account, java.lang.String authority, boolean sync) { throw new RuntimeException(\"Stub!\"); }\n    public static  void addPeriodicSync(android.accounts.Account account, java.lang.String authority, android.os.Bundle extras, long pollFrequency) { throw new RuntimeException(\"Stub!\"); }\n    public static  void removePeriodicSync(android.accounts.Account account, java.lang.String authority, android.os.Bundle extras) { throw new RuntimeException(\"Stub!\"); }\n    public static  java.util.List<android.content.PeriodicSync> getPeriodicSyncs(android.accounts.Account account, java.lang.String authority) { throw new RuntimeException(\"Stub!\"); }\n    public static  int getIsSyncable(android.accounts.Account account, java.lang.String authority) { throw new RuntimeException(\"Stub!\"); }\n    public static  void setIsSyncable(android.accounts.Account account, java.lang.String authority, int syncable) { throw new RuntimeException(\"Stub!\"); }\n    public static  boolean getMasterSyncAutomatically() { throw new RuntimeException(\"Stub!\"); }\n    public static  void setMasterSyncAutomatically(boolean sync) { throw new RuntimeException(\"Stub!\"); }\n    public static  boolean isSyncActive(android.accounts.Account account, java.lang.String authority) { throw new RuntimeException(\"Stub!\"); }\n    @java.lang.Deprecated()\n    public static  android.content.SyncInfo getCurrentSync() { throw new RuntimeException(\"Stub!\"); }\n    public static  java.util.List<android.content.SyncInfo> getCurrentSyncs() { throw new RuntimeException(\"Stub!\"); }\n    public static  boolean isSyncPending(android.accounts.Account account, java.lang.String authority) { throw new RuntimeException(\"Stub!\"); }\n    public static  java.lang.Object addStatusChangeListener(int mask, android.content.SyncStatusObserver callback) { throw new RuntimeException(\"Stub!\"); }\n    public static  void removeStatusChangeListener(java.lang.Object handle) { throw new RuntimeException(\"Stub!\"); }\n    @java.lang.Deprecated()\n    public static final java.lang.String SYNC_EXTRAS_ACCOUNT = \"account\";\n    public static final java.lang.String SYNC_EXTRAS_EXPEDITED = \"expedited\";\n    @java.lang.Deprecated()\n    public static final java.lang.String SYNC_EXTRAS_FORCE = \"force\";\n    public static final java.lang.String SYNC_EXTRAS_IGNORE_SETTINGS = \"ignore_settings\";\n    public static final java.lang.String SYNC_EXTRAS_IGNORE_BACKOFF = \"ignore_backoff\";\n    public static final java.lang.String SYNC_EXTRAS_DO_NOT_RETRY = \"do_not_retry\";\n    public static final java.lang.String SYNC_EXTRAS_MANUAL = \"force\";\n    public static final java.lang.String SYNC_EXTRAS_UPLOAD = \"upload\";\n    public static final java.lang.String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = \"deletions_override\";\n    public static final java.lang.String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = \"discard_deletions\";\n    public static final java.lang.String SYNC_EXTRAS_INITIALIZE = \"initialize\";\n    public static final java.lang.String SCHEME_CONTENT = \"content\";\n    public static final java.lang.String SCHEME_ANDROID_RESOURCE = \"android.resource\";\n    public static final java.lang.String SCHEME_FILE = \"file\";\n    public static final java.lang.String CURSOR_ITEM_BASE_TYPE = \"vnd.android.cursor.item\";\n    public static final java.lang.String CURSOR_DIR_BASE_TYPE = \"vnd.android.cursor.dir\";\n    public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1;\n    public static final int SYNC_OBSERVER_TYPE_PENDING = 2;\n    public static final int SYNC_OBSERVER_TYPE_ACTIVE = 4;\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/content/Intent.java",
    "content": "package android.content;\n\nimport android.net.Uri;\nimport android.os.Bundle;\n\nimport java.util.HashMap;\n\npublic class Intent implements android.os.Parcelable, Cloneable {\n    \n    private String action;\n    private HashMap<String, Object> extras = new HashMap<>();\n\n    public static class ShortcutIconResource implements android.os.Parcelable {\n        public  ShortcutIconResource() { }\n        public static  Intent.ShortcutIconResource fromContext(android.content.Context context, int resourceId) { throw new RuntimeException(\"<Stub>!\"); }\n        public  int describeContents() { throw new RuntimeException(\"<Stub>!\"); }\n        public  void writeToParcel(android.os.Parcel dest, int flags) { throw new RuntimeException(\"<Stub>!\"); }\n        public  String toString() { throw new RuntimeException(\"<Stub>!\"); }\n        public String packageName;\n        public String resourceName;\n        public static final android.os.Parcelable.Creator<Intent.ShortcutIconResource> CREATOR;\n        static { CREATOR = null; }\n    }\n    public static final class FilterComparison {\n        public  FilterComparison(Intent intent) { throw new RuntimeException(\"<Stub>!\"); }\n        public  Intent getIntent() { throw new RuntimeException(\"<Stub>!\"); }\n        public  boolean equals(Object obj) { throw new RuntimeException(\"<Stub>!\"); }\n        public  int hashCode() { throw new RuntimeException(\"<Stub>!\"); }\n    }\n    public  Intent() {  }\n    public  Intent(Intent o) { }\n    public  Intent(String action) { this.action = action; }\n    public  Intent(String action, Uri uri) { this.action = action; }\n    public  Intent(android.content.Context packageContext, Class<?> cls) { }\n    public  Intent(String action, Uri uri, android.content.Context packageContext, Class<?> cls) { this.action = action; }\n    public static  Intent createChooser(Intent target, CharSequence title) { throw new RuntimeException(\"<Stub>!\"); }\n    public  Object clone() { throw new RuntimeException(\"<Stub>!\"); }\n    public  Intent cloneFilter() { throw new RuntimeException(\"<Stub>!\"); }\n    public static  Intent makeMainActivity(android.content.ComponentName mainActivity) { throw new RuntimeException(\"<Stub>!\"); }\n    public static  Intent makeMainSelectorActivity(String selectorAction, String selectorCategory) { throw new RuntimeException(\"<Stub>!\"); }\n    public static  Intent makeRestartActivityTask(android.content.ComponentName mainActivity) { throw new RuntimeException(\"<Stub>!\"); }\n    @Deprecated()\n    public static  Intent getIntent(String uri) throws java.net.URISyntaxException { throw new RuntimeException(\"<Stub>!\"); }\n    public static  Intent parseUri(String uri, int flags) throws java.net.URISyntaxException { throw new RuntimeException(\"<Stub>!\"); }\n    public static  Intent getIntentOld(String uri) throws java.net.URISyntaxException { throw new RuntimeException(\"<Stub>!\"); }\n    public  String getAction() { return action; }\n    public  Uri getData() { return null; }\n    public  String getDataString() { throw new RuntimeException(\"<Stub>!\"); }\n    public  String getScheme() { throw new RuntimeException(\"<Stub>!\"); }\n    public  String getType() { return \"\"; }\n    public  String resolveType(android.content.Context context) { throw new RuntimeException(\"<Stub>!\"); }\n    public  String resolveType(android.content.ContentResolver resolver) { throw new RuntimeException(\"<Stub>!\"); }\n    public  String resolveTypeIfNeeded(android.content.ContentResolver resolver) { throw new RuntimeException(\"<Stub>!\"); }\n    public  boolean hasCategory(String category) { throw new RuntimeException(\"<Stub>!\"); }\n    public  java.util.Set<String> getCategories() { return new java.util.HashSet<String>(); }\n    public  Intent getSelector() { throw new RuntimeException(\"<Stub>!\"); }\n    public  android.content.ClipData getClipData() { throw new RuntimeException(\"<Stub>!\"); }\n    public  void setExtrasClassLoader(ClassLoader loader) { throw new RuntimeException(\"<Stub>!\"); }\n    public  boolean hasExtra(String name) {\n        return extras.containsKey(name);\n    }\n    public  boolean hasFileDescriptors() { throw new RuntimeException(\"<Stub>!\"); }\n    public  boolean getBooleanExtra(String name, boolean defaultValue) { return true; }\n    public  byte getByteExtra(String name, byte defaultValue) { throw new RuntimeException(\"<Stub>!\"); }\n    public  short getShortExtra(String name, short defaultValue) { throw new RuntimeException(\"<Stub>!\"); }\n    public  char getCharExtra(String name, char defaultValue) { throw new RuntimeException(\"<Stub>!\"); }\n    public  int getIntExtra(String name, int defaultValue) { return 0; }\n    public  long getLongExtra(String name, long defaultValue) { return 0; }\n    public  float getFloatExtra(String name, float defaultValue) { return 0; }\n    public  double getDoubleExtra(String name, double defaultValue) { return 0; }\n    public  String getStringExtra(String name) {\n        return (String) extras.get(name);\n    }\n    public  CharSequence getCharSequenceExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public <T extends android.os.Parcelable> T getParcelableExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  android.os.Parcelable[] getParcelableArrayExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayListExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  java.io.Serializable getSerializableExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  java.util.ArrayList<Integer> getIntegerArrayListExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  java.util.ArrayList<String> getStringArrayListExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  java.util.ArrayList<CharSequence> getCharSequenceArrayListExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  boolean[] getBooleanArrayExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  byte[] getByteArrayExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  short[] getShortArrayExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  char[] getCharArrayExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  int[] getIntArrayExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  long[] getLongArrayExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  float[] getFloatArrayExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  double[] getDoubleArrayExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  String[] getStringArrayExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  CharSequence[] getCharSequenceArrayExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  Bundle getBundleExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  Bundle getExtras() { return null; }\n    public  int getFlags() { return 1; }\n    public  String getPackage() { throw new RuntimeException(\"<Stub>!\"); }\n    public  android.content.ComponentName getComponent() { return new android.content.ComponentName(\"\", \"\"); }\n    public  android.graphics.Rect getSourceBounds() { throw new RuntimeException(\"<Stub>!\"); }\n    public  android.content.ComponentName resolveActivity(android.content.pm.PackageManager pm) { throw new RuntimeException(\"<Stub>!\"); }\n    public  android.content.pm.ActivityInfo resolveActivityInfo(android.content.pm.PackageManager pm, int flags) { throw new RuntimeException(\"<Stub>!\"); }\n    public  Intent setAction(String action) {\n        this.action = action;\n        return this;\n    }\n    public  Intent setData(Uri data) { return this; }\n    public  Intent setDataAndNormalize(Uri data) { return this; }\n    public  Intent setType(String type) { return this; }\n    public  Intent setTypeAndNormalize(String type) { return this; }\n    public  Intent setDataAndType(Uri data, String type) { return this; }\n    public  Intent setDataAndTypeAndNormalize(Uri data, String type) { return this; }\n    public  Intent addCategory(String category) { return this; }\n    public  void removeCategory(String category) { throw new RuntimeException(\"<Stub>!\"); }\n    public  void setSelector(Intent selector) { throw new RuntimeException(\"<Stub>!\"); }\n    public  void setClipData(android.content.ClipData clip) { throw new RuntimeException(\"<Stub>!\"); }\n    public  Intent putExtra(String name, boolean value) { return this; }\n    public  Intent putExtra(String name, byte value) { return this; }\n    public  Intent putExtra(String name, char value) { return this; }\n    public  Intent putExtra(String name, short value) { return this; }\n    public  Intent putExtra(String name, int value) { return this; }\n    public  Intent putExtra(String name, long value) { return this; }\n    public  Intent putExtra(String name, float value) { return this; }\n    public  Intent putExtra(String name, double value) { return this; }\n    public  Intent putExtra(String name, String value) {\n        extras.put(name, value);\n        return this;\n    }\n    public  Intent putExtra(String name, CharSequence value) { return this; }\n    public  Intent putExtra(String name, android.os.Parcelable value) { return this; }\n    public  Intent putExtra(String name, android.os.Parcelable[] value) { return this; }\n    public  Intent putParcelableArrayListExtra(String name, java.util.ArrayList<? extends android.os.Parcelable> value) { return this; }\n    public  Intent putIntegerArrayListExtra(String name, java.util.ArrayList<Integer> value) { return this; }\n    public  Intent putStringArrayListExtra(String name, java.util.ArrayList<String> value) { return this; }\n    public  Intent putCharSequenceArrayListExtra(String name, java.util.ArrayList<CharSequence> value) { return this; }\n    public  Intent putExtra(String name, java.io.Serializable value) { return this; }\n    public  Intent putExtra(String name, boolean[] value) { return this; }\n    public  Intent putExtra(String name, byte[] value) { return this; }\n    public  Intent putExtra(String name, short[] value) { return this; }\n    public  Intent putExtra(String name, char[] value) { return this; }\n    public  Intent putExtra(String name, int[] value) { return this; }\n    public  Intent putExtra(String name, long[] value) { return this; }\n    public  Intent putExtra(String name, float[] value) { return this; }\n    public  Intent putExtra(String name, double[] value) { return this; }\n    public  Intent putExtra(String name, String[] value) { return this; }\n    public  Intent putExtra(String name, CharSequence[] value) { return this; }\n    public  Intent putExtra(String name, Bundle value) { return this; }\n    public  Intent putExtras(Intent src) { return this; }\n    public  Intent putExtras(Bundle extras) { return this; }\n    public  Intent replaceExtras(Intent src) { return this; }\n    public  Intent replaceExtras(Bundle extras) { return this; }\n    public  void removeExtra(String name) { throw new RuntimeException(\"<Stub>!\"); }\n    public  Intent setFlags(int flags) { return this; }\n    public  Intent addFlags(int flags) { throw new RuntimeException(\"<Stub>!\"); }\n    public  Intent setPackage(String packageName) { throw new RuntimeException(\"<Stub>!\"); }\n    public  Intent setComponent(android.content.ComponentName component) { return this; }\n    public  Intent setClassName(android.content.Context packageContext, String className) { throw new RuntimeException(\"<Stub>!\"); }\n    public  Intent setClassName(String packageName, String className) { return this; }\n    public  Intent setClass(android.content.Context packageContext, Class<?> cls) { throw new RuntimeException(\"<Stub>!\"); }\n    public  void setSourceBounds(android.graphics.Rect r) { throw new RuntimeException(\"<Stub>!\"); }\n    public  int fillIn(Intent other, int flags) { return 0; }\n    public  boolean filterEquals(Intent other) { throw new RuntimeException(\"<Stub>!\"); }\n    public  int filterHashCode() { throw new RuntimeException(\"<Stub>!\"); }\n    public  String toString() {\n     return \"intent\";\n    }\n    @Deprecated()\n    public  String toURI() { throw new RuntimeException(\"<Stub>!\"); }\n    public  String toUri(int flags) { throw new RuntimeException(\"<Stub>!\"); }\n    public  int describeContents() { throw new RuntimeException(\"<Stub>!\"); }\n    public  void writeToParcel(android.os.Parcel out, int flags) { throw new RuntimeException(\"<Stub>!\"); }\n    public  void readFromParcel(android.os.Parcel in) { throw new RuntimeException(\"<Stub>!\"); }\n    public static  Intent parseIntent(android.content.res.Resources resources, org.xmlpull.v1.XmlPullParser parser, android.util.AttributeSet attrs) throws org.xmlpull.v1.XmlPullParserException, java.io.IOException { throw new RuntimeException(\"<Stub>!\"); }\n    public static  String normalizeMimeType(String type) { throw new RuntimeException(\"<Stub>!\"); }\n    public static final String ACTION_MAIN = \"android.intent.action.MAIN\";\n    public static final String ACTION_VIEW = \"android.intent.action.VIEW\";\n    public static final String ACTION_DEFAULT = \"android.intent.action.VIEW\";\n    public static final String ACTION_ATTACH_DATA = \"android.intent.action.ATTACH_DATA\";\n    public static final String ACTION_EDIT = \"android.intent.action.EDIT\";\n    public static final String ACTION_INSERT_OR_EDIT = \"android.intent.action.INSERT_OR_EDIT\";\n    public static final String ACTION_PICK = \"android.intent.action.PICK\";\n    public static final String ACTION_CREATE_SHORTCUT = \"android.intent.action.CREATE_SHORTCUT\";\n    public static final String EXTRA_SHORTCUT_INTENT = \"android.intent.extra.shortcut.INTENT\";\n    public static final String EXTRA_SHORTCUT_NAME = \"android.intent.extra.shortcut.NAME\";\n    public static final String EXTRA_SHORTCUT_ICON = \"android.intent.extra.shortcut.ICON\";\n    public static final String EXTRA_SHORTCUT_ICON_RESOURCE = \"android.intent.extra.shortcut.ICON_RESOURCE\";\n    public static final String ACTION_CHOOSER = \"android.intent.action.CHOOSER\";\n    public static final String ACTION_GET_CONTENT = \"android.intent.action.GET_CONTENT\";\n    public static final String ACTION_DIAL = \"android.intent.action.DIAL\";\n    public static final String ACTION_CALL = \"android.intent.action.CALL\";\n    public static final String ACTION_SENDTO = \"android.intent.action.SENDTO\";\n    public static final String ACTION_SEND = \"android.intent.action.SEND\";\n    public static final String ACTION_SEND_MULTIPLE = \"android.intent.action.SEND_MULTIPLE\";\n    public static final String ACTION_ANSWER = \"android.intent.action.ANSWER\";\n    public static final String ACTION_INSERT = \"android.intent.action.INSERT\";\n    public static final String ACTION_PASTE = \"android.intent.action.PASTE\";\n    public static final String ACTION_DELETE = \"android.intent.action.DELETE\";\n    public static final String ACTION_RUN = \"android.intent.action.RUN\";\n    public static final String ACTION_SYNC = \"android.intent.action.SYNC\";\n    public static final String ACTION_PICK_ACTIVITY = \"android.intent.action.PICK_ACTIVITY\";\n    public static final String ACTION_SEARCH = \"android.intent.action.SEARCH\";\n    public static final String ACTION_SYSTEM_TUTORIAL = \"android.intent.action.SYSTEM_TUTORIAL\";\n    public static final String ACTION_WEB_SEARCH = \"android.intent.action.WEB_SEARCH\";\n    public static final String ACTION_ASSIST = \"android.intent.action.ASSIST\";\n    public static final String ACTION_ALL_APPS = \"android.intent.action.ALL_APPS\";\n    public static final String ACTION_SET_WALLPAPER = \"android.intent.action.SET_WALLPAPER\";\n    public static final String ACTION_BUG_REPORT = \"android.intent.action.BUG_REPORT\";\n    public static final String ACTION_FACTORY_TEST = \"android.intent.action.FACTORY_TEST\";\n    public static final String ACTION_CALL_BUTTON = \"android.intent.action.CALL_BUTTON\";\n    public static final String ACTION_VOICE_COMMAND = \"android.intent.action.VOICE_COMMAND\";\n    public static final String ACTION_SEARCH_LONG_PRESS = \"android.intent.action.SEARCH_LONG_PRESS\";\n    public static final String ACTION_APP_ERROR = \"android.intent.action.APP_ERROR\";\n    public static final String ACTION_POWER_USAGE_SUMMARY = \"android.intent.action.POWER_USAGE_SUMMARY\";\n    public static final String ACTION_MANAGE_NETWORK_USAGE = \"android.intent.action.MANAGE_NETWORK_USAGE\";\n    public static final String ACTION_INSTALL_PACKAGE = \"android.intent.action.INSTALL_PACKAGE\";\n    public static final String EXTRA_INSTALLER_PACKAGE_NAME = \"android.intent.extra.INSTALLER_PACKAGE_NAME\";\n    public static final String EXTRA_NOT_UNKNOWN_SOURCE = \"android.intent.extra.NOT_UNKNOWN_SOURCE\";\n    @Deprecated()\n    public static final String EXTRA_ALLOW_REPLACE = \"android.intent.extra.ALLOW_REPLACE\";\n    public static final String EXTRA_RETURN_RESULT = \"android.intent.extra.RETURN_RESULT\";\n    public static final String ACTION_UNINSTALL_PACKAGE = \"android.intent.action.UNINSTALL_PACKAGE\";\n    public static final String ACTION_SCREEN_OFF = \"android.intent.action.SCREEN_OFF\";\n    public static final String ACTION_SCREEN_ON = \"android.intent.action.SCREEN_ON\";\n    public static final String ACTION_USER_PRESENT = \"android.intent.action.USER_PRESENT\";\n    public static final String ACTION_TIME_TICK = \"android.intent.action.TIME_TICK\";\n    public static final String ACTION_TIME_CHANGED = \"android.intent.action.TIME_SET\";\n    public static final String ACTION_DATE_CHANGED = \"android.intent.action.DATE_CHANGED\";\n    public static final String ACTION_TIMEZONE_CHANGED = \"android.intent.action.TIMEZONE_CHANGED\";\n    public static final String ACTION_BOOT_COMPLETED = \"android.intent.action.BOOT_COMPLETED\";\n    public static final String ACTION_CLOSE_SYSTEM_DIALOGS = \"android.intent.action.CLOSE_SYSTEM_DIALOGS\";\n    @Deprecated()\n    public static final String ACTION_PACKAGE_INSTALL = \"android.intent.action.PACKAGE_INSTALL\";\n    public static final String ACTION_PACKAGE_ADDED = \"android.intent.action.PACKAGE_ADDED\";\n    public static final String ACTION_PACKAGE_REPLACED = \"android.intent.action.PACKAGE_REPLACED\";\n    public static final String ACTION_MY_PACKAGE_REPLACED = \"android.intent.action.MY_PACKAGE_REPLACED\";\n    public static final String ACTION_PACKAGE_REMOVED = \"android.intent.action.PACKAGE_REMOVED\";\n    public static final String ACTION_PACKAGE_FULLY_REMOVED = \"android.intent.action.PACKAGE_FULLY_REMOVED\";\n    public static final String ACTION_PACKAGE_CHANGED = \"android.intent.action.PACKAGE_CHANGED\";\n    public static final String ACTION_PACKAGE_RESTARTED = \"android.intent.action.PACKAGE_RESTARTED\";\n    public static final String ACTION_PACKAGE_DATA_CLEARED = \"android.intent.action.PACKAGE_DATA_CLEARED\";\n    public static final String ACTION_UID_REMOVED = \"android.intent.action.UID_REMOVED\";\n    public static final String ACTION_PACKAGE_FIRST_LAUNCH = \"android.intent.action.PACKAGE_FIRST_LAUNCH\";\n    public static final String ACTION_PACKAGE_NEEDS_VERIFICATION = \"android.intent.action.PACKAGE_NEEDS_VERIFICATION\";\n    public static final String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = \"android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE\";\n    public static final String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = \"android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE\";\n    @Deprecated()\n    public static final String ACTION_WALLPAPER_CHANGED = \"android.intent.action.WALLPAPER_CHANGED\";\n    public static final String ACTION_CONFIGURATION_CHANGED = \"android.intent.action.CONFIGURATION_CHANGED\";\n    public static final String ACTION_LOCALE_CHANGED = \"android.intent.action.LOCALE_CHANGED\";\n    public static final String ACTION_BATTERY_CHANGED = \"android.intent.action.BATTERY_CHANGED\";\n    public static final String ACTION_BATTERY_LOW = \"android.intent.action.BATTERY_LOW\";\n    public static final String ACTION_BATTERY_OKAY = \"android.intent.action.BATTERY_OKAY\";\n    public static final String ACTION_POWER_CONNECTED = \"android.intent.action.ACTION_POWER_CONNECTED\";\n    public static final String ACTION_POWER_DISCONNECTED = \"android.intent.action.ACTION_POWER_DISCONNECTED\";\n    public static final String ACTION_SHUTDOWN = \"android.intent.action.ACTION_SHUTDOWN\";\n    public static final String ACTION_DEVICE_STORAGE_LOW = \"android.intent.action.DEVICE_STORAGE_LOW\";\n    public static final String ACTION_DEVICE_STORAGE_OK = \"android.intent.action.DEVICE_STORAGE_OK\";\n    public static final String ACTION_MANAGE_PACKAGE_STORAGE = \"android.intent.action.MANAGE_PACKAGE_STORAGE\";\n    @Deprecated()\n    public static final String ACTION_UMS_CONNECTED = \"android.intent.action.UMS_CONNECTED\";\n    @Deprecated()\n    public static final String ACTION_UMS_DISCONNECTED = \"android.intent.action.UMS_DISCONNECTED\";\n    public static final String ACTION_MEDIA_REMOVED = \"android.intent.action.MEDIA_REMOVED\";\n    public static final String ACTION_MEDIA_UNMOUNTED = \"android.intent.action.MEDIA_UNMOUNTED\";\n    public static final String ACTION_MEDIA_CHECKING = \"android.intent.action.MEDIA_CHECKING\";\n    public static final String ACTION_MEDIA_NOFS = \"android.intent.action.MEDIA_NOFS\";\n    public static final String ACTION_MEDIA_MOUNTED = \"android.intent.action.MEDIA_MOUNTED\";\n    public static final String ACTION_MEDIA_SHARED = \"android.intent.action.MEDIA_SHARED\";\n    public static final String ACTION_MEDIA_BAD_REMOVAL = \"android.intent.action.MEDIA_BAD_REMOVAL\";\n    public static final String ACTION_MEDIA_UNMOUNTABLE = \"android.intent.action.MEDIA_UNMOUNTABLE\";\n    public static final String ACTION_MEDIA_EJECT = \"android.intent.action.MEDIA_EJECT\";\n    public static final String ACTION_MEDIA_SCANNER_STARTED = \"android.intent.action.MEDIA_SCANNER_STARTED\";\n    public static final String ACTION_MEDIA_SCANNER_FINISHED = \"android.intent.action.MEDIA_SCANNER_FINISHED\";\n    public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = \"android.intent.action.MEDIA_SCANNER_SCAN_FILE\";\n    public static final String ACTION_MEDIA_BUTTON = \"android.intent.action.MEDIA_BUTTON\";\n    public static final String ACTION_CAMERA_BUTTON = \"android.intent.action.CAMERA_BUTTON\";\n    public static final String ACTION_GTALK_SERVICE_CONNECTED = \"android.intent.action.GTALK_CONNECTED\";\n    public static final String ACTION_GTALK_SERVICE_DISCONNECTED = \"android.intent.action.GTALK_DISCONNECTED\";\n    public static final String ACTION_INPUT_METHOD_CHANGED = \"android.intent.action.INPUT_METHOD_CHANGED\";\n    public static final String ACTION_AIRPLANE_MODE_CHANGED = \"android.intent.action.AIRPLANE_MODE\";\n    public static final String ACTION_PROVIDER_CHANGED = \"android.intent.action.PROVIDER_CHANGED\";\n    public static final String ACTION_HEADSET_PLUG = \"android.intent.action.HEADSET_PLUG\";\n    public static final String ACTION_NEW_OUTGOING_CALL = \"android.intent.action.NEW_OUTGOING_CALL\";\n    public static final String ACTION_REBOOT = \"android.intent.action.REBOOT\";\n    public static final String ACTION_DOCK_EVENT = \"android.intent.action.DOCK_EVENT\";\n    public static final String CATEGORY_DEFAULT = \"android.intent.category.DEFAULT\";\n    public static final String CATEGORY_BROWSABLE = \"android.intent.category.BROWSABLE\";\n    public static final String CATEGORY_ALTERNATIVE = \"android.intent.category.ALTERNATIVE\";\n    public static final String CATEGORY_SELECTED_ALTERNATIVE = \"android.intent.category.SELECTED_ALTERNATIVE\";\n    public static final String CATEGORY_TAB = \"android.intent.category.TAB\";\n    public static final String CATEGORY_LAUNCHER = \"android.intent.category.LAUNCHER\";\n    public static final String CATEGORY_INFO = \"android.intent.category.INFO\";\n    public static final String CATEGORY_HOME = \"android.intent.category.HOME\";\n    public static final String CATEGORY_PREFERENCE = \"android.intent.category.PREFERENCE\";\n    public static final String CATEGORY_DEVELOPMENT_PREFERENCE = \"android.intent.category.DEVELOPMENT_PREFERENCE\";\n    public static final String CATEGORY_EMBED = \"android.intent.category.EMBED\";\n    public static final String CATEGORY_APP_MARKET = \"android.intent.category.APP_MARKET\";\n    public static final String CATEGORY_MONKEY = \"android.intent.category.MONKEY\";\n    public static final String CATEGORY_TEST = \"android.intent.category.TEST\";\n    public static final String CATEGORY_UNIT_TEST = \"android.intent.category.UNIT_TEST\";\n    public static final String CATEGORY_SAMPLE_CODE = \"android.intent.category.SAMPLE_CODE\";\n    public static final String CATEGORY_OPENABLE = \"android.intent.category.OPENABLE\";\n    public static final String CATEGORY_FRAMEWORK_INSTRUMENTATION_TEST = \"android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST\";\n    public static final String CATEGORY_CAR_DOCK = \"android.intent.category.CAR_DOCK\";\n    public static final String CATEGORY_DESK_DOCK = \"android.intent.category.DESK_DOCK\";\n    public static final String CATEGORY_LE_DESK_DOCK = \"android.intent.category.LE_DESK_DOCK\";\n    public static final String CATEGORY_HE_DESK_DOCK = \"android.intent.category.HE_DESK_DOCK\";\n    public static final String CATEGORY_CAR_MODE = \"android.intent.category.CAR_MODE\";\n    public static final String CATEGORY_APP_BROWSER = \"android.intent.category.APP_BROWSER\";\n    public static final String CATEGORY_APP_CALCULATOR = \"android.intent.category.APP_CALCULATOR\";\n    public static final String CATEGORY_APP_CALENDAR = \"android.intent.category.APP_CALENDAR\";\n    public static final String CATEGORY_APP_CONTACTS = \"android.intent.category.APP_CONTACTS\";\n    public static final String CATEGORY_APP_EMAIL = \"android.intent.category.APP_EMAIL\";\n    public static final String CATEGORY_APP_GALLERY = \"android.intent.category.APP_GALLERY\";\n    public static final String CATEGORY_APP_MAPS = \"android.intent.category.APP_MAPS\";\n    public static final String CATEGORY_APP_MESSAGING = \"android.intent.category.APP_MESSAGING\";\n    public static final String CATEGORY_APP_MUSIC = \"android.intent.category.APP_MUSIC\";\n    public static final String EXTRA_TEMPLATE = \"android.intent.extra.TEMPLATE\";\n    public static final String EXTRA_TEXT = \"android.intent.extra.TEXT\";\n    public static final String EXTRA_HTML_TEXT = \"android.intent.extra.HTML_TEXT\";\n    public static final String EXTRA_STREAM = \"android.intent.extra.STREAM\";\n    public static final String EXTRA_EMAIL = \"android.intent.extra.EMAIL\";\n    public static final String EXTRA_CC = \"android.intent.extra.CC\";\n    public static final String EXTRA_BCC = \"android.intent.extra.BCC\";\n    public static final String EXTRA_SUBJECT = \"android.intent.extra.SUBJECT\";\n    public static final String EXTRA_INTENT = \"android.intent.extra.INTENT\";\n    public static final String EXTRA_TITLE = \"android.intent.extra.TITLE\";\n    public static final String EXTRA_INITIAL_INTENTS = \"android.intent.extra.INITIAL_INTENTS\";\n    public static final String EXTRA_KEY_EVENT = \"android.intent.extra.KEY_EVENT\";\n    public static final String EXTRA_DONT_KILL_APP = \"android.intent.extra.DONT_KILL_APP\";\n    public static final String EXTRA_PHONE_NUMBER = \"android.intent.extra.PHONE_NUMBER\";\n    public static final String EXTRA_UID = \"android.intent.extra.UID\";\n    public static final String EXTRA_DATA_REMOVED = \"android.intent.extra.DATA_REMOVED\";\n    public static final String EXTRA_REPLACING = \"android.intent.extra.REPLACING\";\n    public static final String EXTRA_ALARM_COUNT = \"android.intent.extra.ALARM_COUNT\";\n    public static final String EXTRA_DOCK_STATE = \"android.intent.extra.DOCK_STATE\";\n    public static final int EXTRA_DOCK_STATE_UNDOCKED = 0;\n    public static final int EXTRA_DOCK_STATE_DESK = 1;\n    public static final int EXTRA_DOCK_STATE_CAR = 2;\n    public static final int EXTRA_DOCK_STATE_LE_DESK = 3;\n    public static final int EXTRA_DOCK_STATE_HE_DESK = 4;\n    public static final String METADATA_DOCK_HOME = \"android.dock_home\";\n    public static final String EXTRA_BUG_REPORT = \"android.intent.extra.BUG_REPORT\";\n    public static final String EXTRA_REMOTE_INTENT_TOKEN = \"android.intent.extra.remote_intent_token\";\n    @Deprecated()\n    public static final String EXTRA_CHANGED_COMPONENT_NAME = \"android.intent.extra.changed_component_name\";\n    public static final String EXTRA_CHANGED_COMPONENT_NAME_LIST = \"android.intent.extra.changed_component_name_list\";\n    public static final String EXTRA_CHANGED_PACKAGE_LIST = \"android.intent.extra.changed_package_list\";\n    public static final String EXTRA_CHANGED_UID_LIST = \"android.intent.extra.changed_uid_list\";\n    public static final String EXTRA_LOCAL_ONLY = \"android.intent.extra.LOCAL_ONLY\";\n    public static final int FLAG_GRANT_READ_URI_PERMISSION = 1;\n    public static final int FLAG_GRANT_WRITE_URI_PERMISSION = 2;\n    public static final int FLAG_FROM_BACKGROUND = 4;\n    public static final int FLAG_DEBUG_LOG_RESOLUTION = 8;\n    public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 16;\n    public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 32;\n    public static final int FLAG_ACTIVITY_NO_HISTORY = 1073741824;\n    public static final int FLAG_ACTIVITY_SINGLE_TOP = 536870912;\n    public static final int FLAG_ACTIVITY_NEW_TASK = 268435456;\n    public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728;\n    public static final int FLAG_ACTIVITY_CLEAR_TOP = 67108864;\n    public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432;\n    public static final int FLAG_ACTIVITY_PREVIOUS_IS_TOP = 16777216;\n    public static final int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 8388608;\n    public static final int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 4194304;\n    public static final int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 2097152;\n    public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576;\n    public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 524288;\n    public static final int FLAG_ACTIVITY_NO_USER_ACTION = 262144;\n    public static final int FLAG_ACTIVITY_REORDER_TO_FRONT = 131072;\n    public static final int FLAG_ACTIVITY_NO_ANIMATION = 65536;\n    public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768;\n    public static final int FLAG_ACTIVITY_TASK_ON_HOME = 16384;\n    public static final int FLAG_RECEIVER_REGISTERED_ONLY = 1073741824;\n    public static final int FLAG_RECEIVER_REPLACE_PENDING = 536870912;\n    public static final int FLAG_RECEIVER_FOREGROUND = 268435456;\n    public static final int URI_INTENT_SCHEME = 1;\n    public static final int FILL_IN_ACTION = 1;\n    public static final int FILL_IN_DATA = 2;\n    public static final int FILL_IN_CATEGORIES = 4;\n    public static final int FILL_IN_COMPONENT = 8;\n    public static final int FILL_IN_PACKAGE = 16;\n    public static final int FILL_IN_SOURCE_BOUNDS = 32;\n    public static final int FILL_IN_SELECTOR = 64;\n    public static final int FILL_IN_CLIP_DATA = 128;\n    public static final android.os.Parcelable.Creator<Intent> CREATOR;\n    static { CREATOR = null; }\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/content/res/AssetManager.java",
    "content": "package android.content.res;\n\nimport java.io.IOException;\n\nimport android.content.res.AssetFileDescriptor;\nimport android.content.res.XmlResourceParser;\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class AssetManager implements AutoCloseable {\n    public static final int ACCESS_BUFFER = 3;\n    public static final int ACCESS_RANDOM = 1;\n    public static final int ACCESS_STREAMING = 2;\n    public static final int ACCESS_UNKNOWN = 0;\n\n    AssetManager() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void close() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public InputStream open(String fileName) throws IOException {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final InputStream open(String fileName, int accessMode) throws IOException {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final AssetFileDescriptor openFd(String fileName) throws IOException {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final native String[] list(String var1) throws IOException;\n\n    public final AssetFileDescriptor openNonAssetFd(String fileName) throws IOException {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final AssetFileDescriptor openNonAssetFd(int cookie, String fileName) throws IOException {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final XmlResourceParser openXmlResourceParser(String fileName) throws IOException {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final XmlResourceParser openXmlResourceParser(int cookie, String fileName) throws IOException {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    protected void finalize() throws Throwable {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final native String[] getLocales();\n\n    public final class AssetInputStream extends InputStream {\n        AssetInputStream() {\n            throw new RuntimeException(\"Stub!\");\n        }\n\n        public final int read() throws IOException {\n            throw new RuntimeException(\"Stub!\");\n        }\n\n        public final boolean markSupported() {\n            throw new RuntimeException(\"Stub!\");\n        }\n\n        public final int available() throws IOException {\n            throw new RuntimeException(\"Stub!\");\n        }\n\n        public final void close() throws IOException {\n            throw new RuntimeException(\"Stub!\");\n        }\n\n        public final void mark(int readlimit) {\n            throw new RuntimeException(\"Stub!\");\n        }\n\n        public final void reset() throws IOException {\n            throw new RuntimeException(\"Stub!\");\n        }\n\n        public final int read(byte[] b) throws IOException {\n            throw new RuntimeException(\"Stub!\");\n        }\n\n        public final int read(byte[] b, int off, int len) throws IOException {\n            throw new RuntimeException(\"Stub!\");\n        }\n\n        public final long skip(long n) throws IOException {\n            throw new RuntimeException(\"Stub!\");\n        }\n\n        protected void finalize() throws Throwable {\n            throw new RuntimeException(\"Stub!\");\n        }\n    }\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/database/ContentObserver.java",
    "content": "package android.database;\n\nimport android.net.Uri;\nimport android.os.Handler;\n\npublic abstract class ContentObserver {\n    public ContentObserver(Handler handler) { }\n\n    public boolean deliverSelfNotifications() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void onChange(boolean selfChange) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void onChange(boolean selfChange, Uri uri) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    /** @deprecated */\n    @Deprecated\n    public final void dispatchChange(boolean selfChange) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final void dispatchChange(boolean selfChange, Uri uri) {\n        throw new RuntimeException(\"Stub!\");\n    }\n}"
  },
  {
    "path": "modules/mock-android/src/main/java/android/graphics/Bitmap.java",
    "content": "package android.graphics;\n\npublic class Bitmap\n        implements android.os.Parcelable {\n    public static enum Config {\n        ALPHA_8(),\n        ARGB_4444(),\n        ARGB_8888(),\n        RGB_565();\n    }\n\n    public static enum CompressFormat {\n        JPEG(),\n        PNG(),\n        WEBP();\n    }\n\n    Bitmap() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getDensity() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void setDensity(int density) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void recycle() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final boolean isRecycled() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getGenerationId() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void copyPixelsToBuffer(java.nio.Buffer dst) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void copyPixelsFromBuffer(java.nio.Buffer src) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public Bitmap copy(Config config, boolean isMutable) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Bitmap createBitmap(Bitmap src) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Bitmap createBitmap(int width, int height, Config config) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Bitmap createBitmap(int[] colors, int offset, int stride, int width, int height, Config config) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Bitmap createBitmap(int[] colors, int width, int height, Config config) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public byte[] getNinePatchChunk() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean compress(CompressFormat format, int quality, java.io.OutputStream stream) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final boolean isMutable() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final int getWidth() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final int getHeight() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getScaledWidth(Canvas canvas) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getScaledHeight(Canvas canvas) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getScaledWidth(android.util.DisplayMetrics metrics) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getScaledHeight(android.util.DisplayMetrics metrics) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getScaledWidth(int targetDensity) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getScaledHeight(int targetDensity) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final int getRowBytes() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final int getByteCount() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final Config getConfig() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final boolean hasAlpha() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void setHasAlpha(boolean hasAlpha) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void eraseColor(int c) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getPixel(int x, int y) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void getPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void setPixel(int x, int y, int color) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void setPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int describeContents() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void writeToParcel(android.os.Parcel p, int flags) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public Bitmap extractAlpha() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public Bitmap extractAlpha(Paint paint, int[] offsetXY) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean sameAs(Bitmap other) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void prepareToDraw() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static final int DENSITY_NONE = 0;\n    public static final Creator<Bitmap> CREATOR;\n\n    static {\n        CREATOR = null;\n    }\n}"
  },
  {
    "path": "modules/mock-android/src/main/java/android/graphics/Color.java",
    "content": "package android.graphics;\n\nimport java.util.Locale;\n\npublic class Color\n{\n    public  Color() { throw new RuntimeException(\"Stub!\"); }\n    public static  int alpha(int color) { throw new RuntimeException(\"Stub!\"); }\n    public static  int red(int color) { throw new RuntimeException(\"Stub!\"); }\n    public static  int green(int color) { throw new RuntimeException(\"Stub!\"); }\n    public static  int blue(int color) { throw new RuntimeException(\"Stub!\"); }\n    public static  int rgb(int red, int green, int blue) { throw new RuntimeException(\"Stub!\"); }\n    public static  int argb(int alpha, int red, int green, int blue) { throw new RuntimeException(\"Stub!\"); }\n    public static  int parseColor(java.lang.String colorString) {\n        if (colorString.charAt(0) == '#') {\n            // Use a long to avoid rollovers on #ffXXXXXX\n            long color = Long.parseLong(colorString.substring(1), 16);\n            if (colorString.length() == 7) {\n                // Set the alpha value\n                color |= 0x00000000ff000000;\n            } else if (colorString.length() != 9) {\n                throw new IllegalArgumentException(\"Unknown color\");\n            }\n            return (int) color;\n        } else {\n            throw new IllegalArgumentException(\"Unknown color\");\n        }\n    }\n    public static  void RGBToHSV(int red, int green, int blue, float[] hsv) { throw new RuntimeException(\"Stub!\"); }\n    public static  void colorToHSV(int color, float[] hsv) { throw new RuntimeException(\"Stub!\"); }\n    public static  int HSVToColor(float[] hsv) { throw new RuntimeException(\"Stub!\"); }\n    public static  int HSVToColor(int alpha, float[] hsv) { throw new RuntimeException(\"Stub!\"); }\n    public static final int BLACK = -16777216;\n    public static final int DKGRAY = -12303292;\n    public static final int GRAY = -7829368;\n    public static final int LTGRAY = -3355444;\n    public static final int WHITE = -1;\n    public static final int RED = -65536;\n    public static final int GREEN = -16711936;\n    public static final int BLUE = -16776961;\n    public static final int YELLOW = -256;\n    public static final int CYAN = -16711681;\n    public static final int MAGENTA = -65281;\n    public static final int TRANSPARENT = 0;\n}"
  },
  {
    "path": "modules/mock-android/src/main/java/android/graphics/Rect.java",
    "content": "package android.graphics;\n\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.os.Parcelable.Creator;\n\npublic class Rect implements Parcelable {\n    public static final Creator<Rect> CREATOR = null;\n    public int bottom;\n    public int left;\n    public int right;\n    public int top;\n\n    public Rect() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public Rect(int left, int top, int right, int bottom) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public Rect(Rect r) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean equals(Object o) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int hashCode() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public String toString() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public String toShortString() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public String flattenToString() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Rect unflattenFromString(String str) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final boolean isEmpty() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final int width() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final int height() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final int centerX() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final int centerY() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final float exactCenterX() {\n        return 53;\n    }\n\n    public final float exactCenterY() {\n        return 53;\n    }\n\n    public void setEmpty() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void set(int left, int top, int right, int bottom) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void set(Rect src) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void offset(int dx, int dy) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void offsetTo(int newLeft, int newTop) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void inset(int dx, int dy) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean contains(int x, int y) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean contains(int left, int top, int right, int bottom) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean contains(Rect r) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean intersect(int left, int top, int right, int bottom) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean intersect(Rect r) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean setIntersect(Rect a, Rect b) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean intersects(int left, int top, int right, int bottom) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static boolean intersects(Rect a, Rect b) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void union(int left, int top, int right, int bottom) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void union(Rect r) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void union(int x, int y) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void sort() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int describeContents() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void writeToParcel(Parcel out, int flags) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void readFromParcel(Parcel in) {\n        throw new RuntimeException(\"Stub!\");\n    }\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/graphics/drawable/Drawable.java",
    "content": "package android.graphics.drawable;\n\nimport android.content.res.Resources;\nimport android.graphics.BitmapFactory.Options;\nimport android.graphics.Canvas;\nimport android.graphics.ColorFilter;\nimport android.graphics.PorterDuff.Mode;\nimport android.graphics.Rect;\nimport android.graphics.Region;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport org.xmlpull.v1.XmlPullParser;\nimport org.xmlpull.v1.XmlPullParserException;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic abstract class Drawable {\n    public Drawable() {\n    }\n\n    public abstract void draw(Canvas var1);\n\n    public void setBounds(int left, int top, int right, int bottom) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void setBounds(Rect bounds) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final void copyBounds(Rect bounds) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final Rect copyBounds() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final Rect getBounds() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void setChangingConfigurations(int configs) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getChangingConfigurations() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void setDither(boolean dither) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void setFilterBitmap(boolean filter) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final void setCallback(Callback cb) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public Callback getCallback() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void invalidateSelf() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void scheduleSelf(Runnable what, long when) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void unscheduleSelf(Runnable what) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public abstract void setAlpha(int var1);\n\n    public abstract void setColorFilter(ColorFilter var1);\n\n    public void setColorFilter(int color, Mode mode) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void clearColorFilter() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean isStateful() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean setState(int[] stateSet) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int[] getState() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void jumpToCurrentState() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public Drawable getCurrent() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final boolean setLevel(int level) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final int getLevel() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean setVisible(boolean visible, boolean restart) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public final boolean isVisible() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public abstract int getOpacity();\n\n    public static int resolveOpacity(int op1, int op2) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public Region getTransparentRegion() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    protected boolean onStateChange(int[] state) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    protected boolean onLevelChange(int level) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    protected void onBoundsChange(Rect bounds) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getIntrinsicWidth() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getIntrinsicHeight() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getMinimumWidth() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public int getMinimumHeight() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public boolean getPadding(Rect padding) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public Drawable mutate() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Drawable createFromStream(InputStream is, String srcName) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Drawable createFromResourceStream(Resources res, TypedValue value, InputStream is, String srcName) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Drawable createFromResourceStream(Resources res, TypedValue value, InputStream is, String srcName, Options opts) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Drawable createFromXml(Resources r, XmlPullParser parser) throws XmlPullParserException, IOException {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public static Drawable createFromPath(String pathName) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public ConstantState getConstantState() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public abstract static class ConstantState {\n        public ConstantState() {\n            throw new RuntimeException(\"Stub!\");\n        }\n\n        public abstract Drawable newDrawable();\n\n        public Drawable newDrawable(Resources res) {\n            throw new RuntimeException(\"Stub!\");\n        }\n\n        public abstract int getChangingConfigurations();\n    }\n\n    public interface Callback {\n        void invalidateDrawable(Drawable var1);\n\n        void scheduleDrawable(Drawable var1, Runnable var2, long var3);\n\n        void unscheduleDrawable(Drawable var1, Runnable var2);\n    }\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/media/ThumbnailUtils.java",
    "content": "package android.media;\n\npublic class ThumbnailUtils {\n    public ThumbnailUtils() {\n    }\n\n    public static android.graphics.Bitmap createVideoThumbnail(java.lang.String filePath, int kind) {\n        return null;\n    }\n\n    public static android.graphics.Bitmap extractThumbnail(android.graphics.Bitmap source, int width, int height) {\n        return null;\n    }\n\n    public static android.graphics.Bitmap extractThumbnail(android.graphics.Bitmap source, int width, int height, int options) {\n        return null;\n    }\n\n    public static final int OPTIONS_RECYCLE_INPUT = 2;\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/net/wifi/WifiConfiguration.java",
    "content": "package android.net.wifi;\n\npublic class WifiConfiguration {\n\n    public String SSID;\n\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/os/Bundle.java",
    "content": "package android.os;\n\nimport android.util.SparseArray;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Set;\npublic class Bundle implements Parcelable, Cloneable {\npublic static final Bundle EMPTY = null;\npublic static final Creator<Bundle> CREATOR = null;\npublic Bundle() {  }\npublic Bundle(ClassLoader loader) { throw new RuntimeException(\"Stub!\"); }\npublic Bundle(int capacity) { throw new RuntimeException(\"Stub!\"); }\npublic Bundle(Bundle b) { throw new RuntimeException(\"Stub!\"); }\npublic void setClassLoader(ClassLoader loader) { throw new RuntimeException(\"Stub!\"); }\npublic ClassLoader getClassLoader() { throw new RuntimeException(\"Stub!\"); }\npublic Object clone() { throw new RuntimeException(\"Stub!\"); }\npublic int size() { throw new RuntimeException(\"Stub!\"); }\npublic boolean isEmpty() { throw new RuntimeException(\"Stub!\"); }\npublic void clear() { throw new RuntimeException(\"Stub!\"); }\npublic boolean containsKey(String key) { throw new RuntimeException(\"Stub!\"); }\npublic Object get(String key) { throw new RuntimeException(\"Stub!\"); }\npublic void remove(String key) { throw new RuntimeException(\"Stub!\"); }\npublic void putAll(Bundle map) { throw new RuntimeException(\"Stub!\"); }\npublic Set<String> keySet() { throw new RuntimeException(\"Stub!\"); }\npublic boolean hasFileDescriptors() { throw new RuntimeException(\"Stub!\"); }\npublic void putBoolean(String key, boolean value) { throw new RuntimeException(\"Stub!\"); }\npublic void putByte(String key, byte value) { throw new RuntimeException(\"Stub!\"); }\npublic void putChar(String key, char value) { throw new RuntimeException(\"Stub!\"); }\npublic void putShort(String key, short value) { throw new RuntimeException(\"Stub!\"); }\npublic void putInt(String key, int value) { }\npublic void putLong(String key, long value) { throw new RuntimeException(\"Stub!\"); }\npublic void putFloat(String key, float value) { throw new RuntimeException(\"Stub!\"); }\npublic void putDouble(String key, double value) { throw new RuntimeException(\"Stub!\"); }\npublic void putString(String key, String value) { }\npublic void putCharSequence(String key, CharSequence value) { throw new RuntimeException(\"Stub!\"); }\npublic void putParcelable(String key, Parcelable value) { throw new RuntimeException(\"Stub!\"); }\npublic void putParcelableArray(String key, Parcelable[] value) { throw new RuntimeException(\"Stub!\"); }\npublic void putParcelableArrayList(String key, ArrayList<? extends Parcelable> value) { throw new RuntimeException(\"Stub!\"); }\npublic void putSparseParcelableArray(String key, SparseArray<? extends Parcelable> value) { throw new RuntimeException(\"Stub!\"); }\npublic void putIntegerArrayList(String key, ArrayList<Integer> value) { throw new RuntimeException(\"Stub!\"); }\npublic void putStringArrayList(String key, ArrayList<String> value) { throw new RuntimeException(\"Stub!\"); }\npublic void putCharSequenceArrayList(String key, ArrayList<CharSequence> value) { throw new RuntimeException(\"Stub!\"); }\npublic void putSerializable(String key, Serializable value) { throw new RuntimeException(\"Stub!\"); }\npublic void putBooleanArray(String key, boolean[] value) { throw new RuntimeException(\"Stub!\"); }\npublic void putByteArray(String key, byte[] value) { throw new RuntimeException(\"Stub!\"); }\npublic void putShortArray(String key, short[] value) { throw new RuntimeException(\"Stub!\"); }\npublic void putCharArray(String key, char[] value) { throw new RuntimeException(\"Stub!\"); }\npublic void putIntArray(String key, int[] value) { throw new RuntimeException(\"Stub!\"); }\npublic void putLongArray(String key, long[] value) { throw new RuntimeException(\"Stub!\"); }\npublic void putFloatArray(String key, float[] value) { throw new RuntimeException(\"Stub!\"); }\npublic void putDoubleArray(String key, double[] value) { throw new RuntimeException(\"Stub!\"); }\npublic void putStringArray(String key, String[] value) {  }\npublic void putCharSequenceArray(String key, CharSequence[] value) { throw new RuntimeException(\"Stub!\"); }\npublic void putBundle(String key, Bundle value) { throw new RuntimeException(\"Stub!\"); }\npublic boolean getBoolean(String key) { throw new RuntimeException(\"Stub!\"); }\npublic boolean getBoolean(String key, boolean defaultValue) { throw new RuntimeException(\"Stub!\"); }\npublic byte getByte(String key) { throw new RuntimeException(\"Stub!\"); }\npublic Byte getByte(String key, byte defaultValue) { throw new RuntimeException(\"Stub!\"); }\npublic char getChar(String key) { throw new RuntimeException(\"Stub!\"); }\npublic char getChar(String key, char defaultValue) { throw new RuntimeException(\"Stub!\"); }\npublic short getShort(String key) { throw new RuntimeException(\"Stub!\"); }\npublic short getShort(String key, short defaultValue) { throw new RuntimeException(\"Stub!\"); }\npublic int getInt(String key) { throw new RuntimeException(\"Stub!\"); }\npublic int getInt(String key, int defaultValue) { throw new RuntimeException(\"Stub!\"); }\npublic long getLong(String key) { throw new RuntimeException(\"Stub!\"); }\npublic long getLong(String key, long defaultValue) { throw new RuntimeException(\"Stub!\"); }\npublic float getFloat(String key) { throw new RuntimeException(\"Stub!\"); }\npublic float getFloat(String key, float defaultValue) { throw new RuntimeException(\"Stub!\"); }\npublic double getDouble(String key) { throw new RuntimeException(\"Stub!\"); }\npublic double getDouble(String key, double defaultValue) { throw new RuntimeException(\"Stub!\"); }\npublic String getString(String key) { throw new RuntimeException(\"Stub!\"); }\npublic String getString(String key, String defaultValue) { throw new RuntimeException(\"Stub!\"); }\npublic CharSequence getCharSequence(String key) { throw new RuntimeException(\"Stub!\"); }\npublic CharSequence getCharSequence(String key, CharSequence defaultValue) { throw new RuntimeException(\"Stub!\"); }\npublic Bundle getBundle(String key) { throw new RuntimeException(\"Stub!\"); }\npublic <T extends Parcelable> T getParcelable(String key) { throw new RuntimeException(\"Stub!\"); }\npublic Parcelable[] getParcelableArray(String key) { throw new RuntimeException(\"Stub!\"); }\npublic <T extends Parcelable> ArrayList<T> getParcelableArrayList(String key) { throw new RuntimeException(\"Stub!\"); }\npublic <T extends Parcelable> SparseArray<T> getSparseParcelableArray(String key) { throw new RuntimeException(\"Stub!\"); }\npublic Serializable getSerializable(String key) { throw new RuntimeException(\"Stub!\"); }\npublic ArrayList<Integer> getIntegerArrayList(String key) { throw new RuntimeException(\"Stub!\"); }\npublic ArrayList<String> getStringArrayList(String key) { throw new RuntimeException(\"Stub!\"); }\npublic ArrayList<CharSequence> getCharSequenceArrayList(String key) { throw new RuntimeException(\"Stub!\"); }\npublic boolean[] getBooleanArray(String key) { throw new RuntimeException(\"Stub!\"); }\npublic byte[] getByteArray(String key) { throw new RuntimeException(\"Stub!\"); }\npublic short[] getShortArray(String key) { throw new RuntimeException(\"Stub!\"); }\npublic char[] getCharArray(String key) { throw new RuntimeException(\"Stub!\"); }\npublic int[] getIntArray(String key) { throw new RuntimeException(\"Stub!\"); }\npublic long[] getLongArray(String key) { throw new RuntimeException(\"Stub!\"); }\npublic float[] getFloatArray(String key) { throw new RuntimeException(\"Stub!\"); }\npublic double[] getDoubleArray(String key) { throw new RuntimeException(\"Stub!\"); }\npublic String[] getStringArray(String key) { throw new RuntimeException(\"Stub!\"); }\npublic CharSequence[] getCharSequenceArray(String key) { throw new RuntimeException(\"Stub!\"); }\npublic int describeContents() { throw new RuntimeException(\"Stub!\"); }\npublic void writeToParcel(Parcel parcel, int flags) { throw new RuntimeException(\"Stub!\"); }\npublic void readFromParcel(Parcel parcel) { throw new RuntimeException(\"Stub!\"); }\npublic synchronized String toString() { throw new RuntimeException(\"Stub!\"); }\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/os/Handler.java",
    "content": "package android.os;\npublic class Handler\n{\n    public static interface Callback\n    {\n        public abstract  boolean handleMessage(android.os.Message msg);\n    }\n    public  Handler() { throw new RuntimeException(\"Stub!\"); }\n    public  Handler(android.os.Handler.Callback callback) { throw new RuntimeException(\"Stub!\"); }\n    public  Handler(android.os.Looper looper) {  }\n    public  Handler(android.os.Looper looper, android.os.Handler.Callback callback) { throw new RuntimeException(\"Stub!\"); }\n    public  void handleMessage(android.os.Message msg) { throw new RuntimeException(\"Stub!\"); }\n    public  void dispatchMessage(android.os.Message msg) { throw new RuntimeException(\"Stub!\"); }\n    public  java.lang.String getMessageName(android.os.Message message) { throw new RuntimeException(\"Stub!\"); }\n    public final  android.os.Message obtainMessage() { throw new RuntimeException(\"Stub!\"); }\n    public final  android.os.Message obtainMessage(int what) { throw new RuntimeException(\"Stub!\"); }\n    public final  android.os.Message obtainMessage(int what, java.lang.Object obj) { throw new RuntimeException(\"Stub!\"); }\n    public final  android.os.Message obtainMessage(int what, int arg1, int arg2) { throw new RuntimeException(\"Stub!\"); }\n    public final  android.os.Message obtainMessage(int what, int arg1, int arg2, java.lang.Object obj) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean post(java.lang.Runnable r) { return true; }\n    public final  boolean postAtTime(java.lang.Runnable r, long uptimeMillis) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean postAtTime(java.lang.Runnable r, java.lang.Object token, long uptimeMillis) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean postDelayed(java.lang.Runnable r, long delayMillis) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean postAtFrontOfQueue(java.lang.Runnable r) { throw new RuntimeException(\"Stub!\"); }\n    public final  void removeCallbacks(java.lang.Runnable r) { throw new RuntimeException(\"Stub!\"); }\n    public final  void removeCallbacks(java.lang.Runnable r, java.lang.Object token) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean sendMessage(android.os.Message msg) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean sendEmptyMessage(int what) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean sendEmptyMessageDelayed(int what, long delayMillis) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean sendMessageDelayed(android.os.Message msg, long delayMillis) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean sendMessageAtTime(android.os.Message msg, long uptimeMillis) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean sendMessageAtFrontOfQueue(android.os.Message msg) { throw new RuntimeException(\"Stub!\"); }\n    public final  void removeMessages(int what) { throw new RuntimeException(\"Stub!\"); }\n    public final  void removeMessages(int what, java.lang.Object object) { throw new RuntimeException(\"Stub!\"); }\n    public final  void removeCallbacksAndMessages(java.lang.Object token) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean hasMessages(int what) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean hasMessages(int what, java.lang.Object object) { throw new RuntimeException(\"Stub!\"); }\n    public final  android.os.Looper getLooper() { throw new RuntimeException(\"Stub!\"); }\n    public final  void dump(android.util.Printer pw, java.lang.String prefix) { throw new RuntimeException(\"Stub!\"); }\n    public  java.lang.String toString() { throw new RuntimeException(\"Stub!\"); }\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/os/Looper.java",
    "content": "package android.os;\n\npublic class Looper\n{\n    Looper() {  }\n    public static  void prepare() {  }\n    public static  void prepareMainLooper() { }\n    public static  android.os.Looper getMainLooper() { return new Looper(); }\n    public static  void loop() { throw new RuntimeException(\"Stub!\"); }\n    public static  android.os.Looper myLooper() { throw new RuntimeException(\"Stub!\"); }\n    public  void setMessageLogging(android.util.Printer printer) { throw new RuntimeException(\"Stub!\"); }\n    public static  android.os.MessageQueue myQueue() { throw new RuntimeException(\"Stub!\"); }\n    public  void quit() { throw new RuntimeException(\"Stub!\"); }\n    public  java.lang.Thread getThread() { return new Thread(); }\n    public  void dump(android.util.Printer pw, java.lang.String prefix) { throw new RuntimeException(\"Stub!\"); }\n    public  java.lang.String toString() { throw new RuntimeException(\"Stub!\"); }\n}"
  },
  {
    "path": "modules/mock-android/src/main/java/android/util/Log.java",
    "content": "package android.util;\npublic final class Log\n{\n    Log() { throw new RuntimeException(\"Stub!\"); }\n    public static  int v(java.lang.String tag, java.lang.String msg) { throw new RuntimeException(\"Stub!\"); }\n    public static  int v(java.lang.String tag, java.lang.String msg, java.lang.Throwable tr) { throw new RuntimeException(\"Stub!\"); }\n    public static  int d(java.lang.String tag, java.lang.String msg) { throw new RuntimeException(\"Stub!\"); }\n    public static  int d(java.lang.String tag, java.lang.String msg, java.lang.Throwable tr) { throw new RuntimeException(\"Stub!\"); }\n    public static  int i(java.lang.String tag, java.lang.String msg) { return 1; }\n    public static  int i(java.lang.String tag, java.lang.String msg, java.lang.Throwable tr) { throw new RuntimeException(\"Stub!\"); }\n    public static  int w(java.lang.String tag, java.lang.String msg) { throw new RuntimeException(\"Stub!\"); }\n    public static  int w(java.lang.String tag, java.lang.String msg, java.lang.Throwable tr) { throw new RuntimeException(\"Stub!\"); }\n    public static native  boolean isLoggable(java.lang.String tag, int level);\n    public static  int w(java.lang.String tag, java.lang.Throwable tr) { throw new RuntimeException(\"Stub!\"); }\n    public static  int e(java.lang.String tag, java.lang.String msg) { throw new RuntimeException(\"Stub!\"); }\n    public static  int e(java.lang.String tag, java.lang.String msg, java.lang.Throwable tr) { throw new RuntimeException(\"Stub!\"); }\n    public static  int wtf(java.lang.String tag, java.lang.String msg) { throw new RuntimeException(\"Stub!\"); }\n    public static  int wtf(java.lang.String tag, java.lang.Throwable tr) { throw new RuntimeException(\"Stub!\"); }\n    public static  int wtf(java.lang.String tag, java.lang.String msg, java.lang.Throwable tr) { throw new RuntimeException(\"Stub!\"); }\n    public static  java.lang.String getStackTraceString(java.lang.Throwable tr) { throw new RuntimeException(\"Stub!\"); }\n    public static  int println(int priority, java.lang.String tag, java.lang.String msg) { throw new RuntimeException(\"Stub!\"); }\n    public static final int VERBOSE = 2;\n    public static final int DEBUG = 3;\n    public static final int INFO = 4;\n    public static final int WARN = 5;\n    public static final int ERROR = 6;\n    public static final int ASSERT = 7;\n}\n"
  },
  {
    "path": "modules/mock-android/src/main/java/android/view/SearchEvent.java",
    "content": "package android.view;\npublic class SearchEvent {\npublic SearchEvent(InputDevice inputDevice) { throw new RuntimeException(\"Stub!\");}\npublic InputDevice getInputDevice() { throw new RuntimeException(\"Stub!\"); }\n}"
  },
  {
    "path": "modules/mock-android/src/main/java/android/view/View.java",
    "content": "package android.view;\npublic class View\n        implements android.graphics.drawable.Drawable.Callback, android.view.KeyEvent.Callback, android.view.accessibility.AccessibilityEventSource\n{\n    public static interface OnLayoutChangeListener\n    {\n        public abstract  void onLayoutChange(android.view.View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom);\n    }\n    public static class DragShadowBuilder\n    {\n        public  DragShadowBuilder(android.view.View view) { throw new RuntimeException(\"Stub!\"); }\n        public  DragShadowBuilder() { throw new RuntimeException(\"Stub!\"); }\n        @java.lang.SuppressWarnings(value={\"JavadocReference\"})\n        public final  android.view.View getView() { throw new RuntimeException(\"Stub!\"); }\n        public  void onProvideShadowMetrics(android.graphics.Point shadowSize, android.graphics.Point shadowTouchPoint) { throw new RuntimeException(\"Stub!\"); }\n        public  void onDrawShadow(android.graphics.Canvas canvas) { throw new RuntimeException(\"Stub!\"); }\n    }\n    public static class MeasureSpec\n    {\n        public  MeasureSpec() { throw new RuntimeException(\"Stub!\"); }\n        public static  int makeMeasureSpec(int size, int mode) { throw new RuntimeException(\"Stub!\"); }\n        public static  int getMode(int measureSpec) { throw new RuntimeException(\"Stub!\"); }\n        public static  int getSize(int measureSpec) { throw new RuntimeException(\"Stub!\"); }\n        public static  java.lang.String toString(int measureSpec) { throw new RuntimeException(\"Stub!\"); }\n        public static final int UNSPECIFIED = 0;\n        public static final int EXACTLY = 1073741824;\n        public static final int AT_MOST = -2147483648;\n    }\n    public static interface OnKeyListener\n    {\n        public abstract  boolean onKey(android.view.View v, int keyCode, android.view.KeyEvent event);\n    }\n    public static interface OnTouchListener\n    {\n        public abstract  boolean onTouch(android.view.View v, android.view.MotionEvent event);\n    }\n    public static interface OnHoverListener\n    {\n        public abstract  boolean onHover(android.view.View v, android.view.MotionEvent event);\n    }\n    public static interface OnGenericMotionListener\n    {\n        public abstract  boolean onGenericMotion(android.view.View v, android.view.MotionEvent event);\n    }\n    public static interface OnLongClickListener\n    {\n        public abstract  boolean onLongClick(android.view.View v);\n    }\n    public static interface OnDragListener\n    {\n        public abstract  boolean onDrag(android.view.View v, android.view.DragEvent event);\n    }\n    public static interface OnFocusChangeListener\n    {\n        public abstract  void onFocusChange(android.view.View v, boolean hasFocus);\n    }\n    public static interface OnClickListener\n    {\n        public abstract  void onClick(android.view.View v);\n    }\n    public static interface OnCreateContextMenuListener\n    {\n        public abstract  void onCreateContextMenu(android.view.ContextMenu menu, android.view.View v, android.view.ContextMenu.ContextMenuInfo menuInfo);\n    }\n    public static interface OnSystemUiVisibilityChangeListener\n    {\n        public abstract  void onSystemUiVisibilityChange(int visibility);\n    }\n    public static interface OnAttachStateChangeListener\n    {\n        public abstract  void onViewAttachedToWindow(android.view.View v);\n        public abstract  void onViewDetachedFromWindow(android.view.View v);\n    }\n    public static class BaseSavedState\n            extends android.view.AbsSavedState\n    {\n        public  BaseSavedState(android.os.Parcel source) { super((android.os.Parcel)null); throw new RuntimeException(\"Stub!\"); }\n        public  BaseSavedState(android.os.Parcelable superState) { super((android.os.Parcel)null); throw new RuntimeException(\"Stub!\"); }\n        public static final android.os.Parcelable.Creator<android.view.View.BaseSavedState> CREATOR;\n        static { CREATOR = null; }\n    }\n    public static class AccessibilityDelegate\n    {\n        public  AccessibilityDelegate() { throw new RuntimeException(\"Stub!\"); }\n        public  void sendAccessibilityEvent(android.view.View host, int eventType) { throw new RuntimeException(\"Stub!\"); }\n        public  boolean performAccessibilityAction(android.view.View host, int action, android.os.Bundle args) { throw new RuntimeException(\"Stub!\"); }\n        public  void sendAccessibilityEventUnchecked(android.view.View host, android.view.accessibility.AccessibilityEvent event) { throw new RuntimeException(\"Stub!\"); }\n        public  boolean dispatchPopulateAccessibilityEvent(android.view.View host, android.view.accessibility.AccessibilityEvent event) { throw new RuntimeException(\"Stub!\"); }\n        public  void onPopulateAccessibilityEvent(android.view.View host, android.view.accessibility.AccessibilityEvent event) { throw new RuntimeException(\"Stub!\"); }\n        public  void onInitializeAccessibilityEvent(android.view.View host, android.view.accessibility.AccessibilityEvent event) { throw new RuntimeException(\"Stub!\"); }\n        public  void onInitializeAccessibilityNodeInfo(android.view.View host, android.view.accessibility.AccessibilityNodeInfo info) { throw new RuntimeException(\"Stub!\"); }\n        public  boolean onRequestSendAccessibilityEvent(android.view.ViewGroup host, android.view.View child, android.view.accessibility.AccessibilityEvent event) { throw new RuntimeException(\"Stub!\"); }\n        public  android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider(android.view.View host) { throw new RuntimeException(\"Stub!\"); }\n    }\n    public  View(android.content.Context context) { throw new RuntimeException(\"Stub!\"); }\n    public  View(android.content.Context context, android.util.AttributeSet attrs) { throw new RuntimeException(\"Stub!\"); }\n    public  View(android.content.Context context, android.util.AttributeSet attrs, int defStyle) { throw new RuntimeException(\"Stub!\"); }\n    protected  void initializeFadingEdge(android.content.res.TypedArray a) { throw new RuntimeException(\"Stub!\"); }\n    public  int getVerticalFadingEdgeLength() { throw new RuntimeException(\"Stub!\"); }\n    public  void setFadingEdgeLength(int length) { throw new RuntimeException(\"Stub!\"); }\n    public  int getHorizontalFadingEdgeLength() { throw new RuntimeException(\"Stub!\"); }\n    public  int getVerticalScrollbarWidth() { throw new RuntimeException(\"Stub!\"); }\n    protected  int getHorizontalScrollbarHeight() { throw new RuntimeException(\"Stub!\"); }\n    protected  void initializeScrollbars(android.content.res.TypedArray a) { throw new RuntimeException(\"Stub!\"); }\n    public  void setVerticalScrollbarPosition(int position) { throw new RuntimeException(\"Stub!\"); }\n    public  int getVerticalScrollbarPosition() { throw new RuntimeException(\"Stub!\"); }\n    public  void setOnFocusChangeListener(android.view.View.OnFocusChangeListener l) { throw new RuntimeException(\"Stub!\"); }\n    public  void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener listener) { throw new RuntimeException(\"Stub!\"); }\n    public  void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener listener) { throw new RuntimeException(\"Stub!\"); }\n    public  void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener listener) { throw new RuntimeException(\"Stub!\"); }\n    public  void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener listener) { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.View.OnFocusChangeListener getOnFocusChangeListener() { throw new RuntimeException(\"Stub!\"); }\n    public  void setOnClickListener(android.view.View.OnClickListener l) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean hasOnClickListeners() { throw new RuntimeException(\"Stub!\"); }\n    public  void setOnLongClickListener(android.view.View.OnLongClickListener l) { throw new RuntimeException(\"Stub!\"); }\n    public  void setOnCreateContextMenuListener(android.view.View.OnCreateContextMenuListener l) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean performClick() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean callOnClick() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean performLongClick() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean showContextMenu() { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.ActionMode startActionMode(android.view.ActionMode.Callback callback) { throw new RuntimeException(\"Stub!\"); }\n    public  void setOnKeyListener(android.view.View.OnKeyListener l) { throw new RuntimeException(\"Stub!\"); }\n    public  void setOnTouchListener(android.view.View.OnTouchListener l) { throw new RuntimeException(\"Stub!\"); }\n    public  void setOnGenericMotionListener(android.view.View.OnGenericMotionListener l) { throw new RuntimeException(\"Stub!\"); }\n    public  void setOnHoverListener(android.view.View.OnHoverListener l) { throw new RuntimeException(\"Stub!\"); }\n    public  void setOnDragListener(android.view.View.OnDragListener l) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean requestRectangleOnScreen(android.graphics.Rect rectangle) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean requestRectangleOnScreen(android.graphics.Rect rectangle, boolean immediate) { throw new RuntimeException(\"Stub!\"); }\n    public  void clearFocus() { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"focus\")\n    public  boolean hasFocus() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean hasFocusable() { throw new RuntimeException(\"Stub!\"); }\n    protected  void onFocusChanged(boolean gainFocus, int direction, android.graphics.Rect previouslyFocusedRect) { throw new RuntimeException(\"Stub!\"); }\n    public  void sendAccessibilityEvent(int eventType) { throw new RuntimeException(\"Stub!\"); }\n    public  void announceForAccessibility(java.lang.CharSequence text) { throw new RuntimeException(\"Stub!\"); }\n    public  void sendAccessibilityEventUnchecked(android.view.accessibility.AccessibilityEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo() { throw new RuntimeException(\"Stub!\"); }\n    public  void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo info) { throw new RuntimeException(\"Stub!\"); }\n    public  void setAccessibilityDelegate(android.view.View.AccessibilityDelegate delegate) { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider() { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"accessibility\")\n    public  java.lang.CharSequence getContentDescription() { throw new RuntimeException(\"Stub!\"); }\n    public  void setContentDescription(java.lang.CharSequence contentDescription) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"focus\")\n    public  boolean isFocused() { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.View findFocus() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isScrollContainer() { throw new RuntimeException(\"Stub!\"); }\n    public  void setScrollContainer(boolean isScrollContainer) { throw new RuntimeException(\"Stub!\"); }\n    public  int getDrawingCacheQuality() { throw new RuntimeException(\"Stub!\"); }\n    public  void setDrawingCacheQuality(int quality) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean getKeepScreenOn() { throw new RuntimeException(\"Stub!\"); }\n    public  void setKeepScreenOn(boolean keepScreenOn) { throw new RuntimeException(\"Stub!\"); }\n    public  int getNextFocusLeftId() { throw new RuntimeException(\"Stub!\"); }\n    public  void setNextFocusLeftId(int nextFocusLeftId) { throw new RuntimeException(\"Stub!\"); }\n    public  int getNextFocusRightId() { throw new RuntimeException(\"Stub!\"); }\n    public  void setNextFocusRightId(int nextFocusRightId) { throw new RuntimeException(\"Stub!\"); }\n    public  int getNextFocusUpId() { throw new RuntimeException(\"Stub!\"); }\n    public  void setNextFocusUpId(int nextFocusUpId) { throw new RuntimeException(\"Stub!\"); }\n    public  int getNextFocusDownId() { throw new RuntimeException(\"Stub!\"); }\n    public  void setNextFocusDownId(int nextFocusDownId) { throw new RuntimeException(\"Stub!\"); }\n    public  int getNextFocusForwardId() { throw new RuntimeException(\"Stub!\"); }\n    public  void setNextFocusForwardId(int nextFocusForwardId) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isShown() { throw new RuntimeException(\"Stub!\"); }\n    protected  boolean fitSystemWindows(android.graphics.Rect insets) { throw new RuntimeException(\"Stub!\"); }\n    public  void setFitsSystemWindows(boolean fitSystemWindows) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean getFitsSystemWindows() { throw new RuntimeException(\"Stub!\"); }\n    public  void requestFitSystemWindows() { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=0,to=\"VISIBLE\"),@android.view.ViewDebug.IntToString(from=4,to=\"INVISIBLE\"),@android.view.ViewDebug.IntToString(from=8,to=\"GONE\")})\n    public  int getVisibility() { throw new RuntimeException(\"Stub!\"); }\n    public  void setVisibility(int visibility) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty()\n    public  boolean isEnabled() { throw new RuntimeException(\"Stub!\"); }\n    public  void setEnabled(boolean enabled) { throw new RuntimeException(\"Stub!\"); }\n    public  void setFocusable(boolean focusable) { throw new RuntimeException(\"Stub!\"); }\n    public  void setFocusableInTouchMode(boolean focusableInTouchMode) { throw new RuntimeException(\"Stub!\"); }\n    public  void setSoundEffectsEnabled(boolean soundEffectsEnabled) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty()\n    public  boolean isSoundEffectsEnabled() { throw new RuntimeException(\"Stub!\"); }\n    public  void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty()\n    public  boolean isHapticFeedbackEnabled() { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"layout\")\n    public  boolean hasTransientState() { throw new RuntimeException(\"Stub!\"); }\n    public  void setHasTransientState(boolean hasTransientState) { throw new RuntimeException(\"Stub!\"); }\n    public  void setWillNotDraw(boolean willNotDraw) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  boolean willNotDraw() { throw new RuntimeException(\"Stub!\"); }\n    public  void setWillNotCacheDrawing(boolean willNotCacheDrawing) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  boolean willNotCacheDrawing() { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty()\n    public  boolean isClickable() { throw new RuntimeException(\"Stub!\"); }\n    public  void setClickable(boolean clickable) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isLongClickable() { throw new RuntimeException(\"Stub!\"); }\n    public  void setLongClickable(boolean longClickable) { throw new RuntimeException(\"Stub!\"); }\n    public  void setPressed(boolean pressed) { throw new RuntimeException(\"Stub!\"); }\n    protected  void dispatchSetPressed(boolean pressed) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isPressed() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isSaveEnabled() { throw new RuntimeException(\"Stub!\"); }\n    public  void setSaveEnabled(boolean enabled) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty()\n    public  boolean getFilterTouchesWhenObscured() { throw new RuntimeException(\"Stub!\"); }\n    public  void setFilterTouchesWhenObscured(boolean enabled) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isSaveFromParentEnabled() { throw new RuntimeException(\"Stub!\"); }\n    public  void setSaveFromParentEnabled(boolean enabled) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"focus\")\n    public final  boolean isFocusable() { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty()\n    public final  boolean isFocusableInTouchMode() { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.View focusSearch(int direction) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean dispatchUnhandledMove(android.view.View focused, int direction) { throw new RuntimeException(\"Stub!\"); }\n    public  java.util.ArrayList<android.view.View> getFocusables(int direction) { throw new RuntimeException(\"Stub!\"); }\n    public  void addFocusables(java.util.ArrayList<android.view.View> views, int direction) { throw new RuntimeException(\"Stub!\"); }\n    public  void addFocusables(java.util.ArrayList<android.view.View> views, int direction, int focusableMode) { throw new RuntimeException(\"Stub!\"); }\n    public  void findViewsWithText(java.util.ArrayList<android.view.View> outViews, java.lang.CharSequence searched, int flags) { throw new RuntimeException(\"Stub!\"); }\n    public  java.util.ArrayList<android.view.View> getTouchables() { throw new RuntimeException(\"Stub!\"); }\n    public  void addTouchables(java.util.ArrayList<android.view.View> views) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean requestFocus() { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean requestFocus(int direction) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean requestFocus(int direction, android.graphics.Rect previouslyFocusedRect) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean requestFocusFromTouch() { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"accessibility\",mapping={@android.view.ViewDebug.IntToString(from=0,to=\"auto\"),@android.view.ViewDebug.IntToString(from=1,to=\"yes\"),@android.view.ViewDebug.IntToString(from=2,to=\"no\")})\n    public  int getImportantForAccessibility() { throw new RuntimeException(\"Stub!\"); }\n    public  void setImportantForAccessibility(int mode) { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.ViewParent getParentForAccessibility() { throw new RuntimeException(\"Stub!\"); }\n    public  void addChildrenForAccessibility(java.util.ArrayList<android.view.View> children) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean performAccessibilityAction(int action, android.os.Bundle arguments) { throw new RuntimeException(\"Stub!\"); }\n    public  void onStartTemporaryDetach() { throw new RuntimeException(\"Stub!\"); }\n    public  void onFinishTemporaryDetach() { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.KeyEvent.DispatcherState getKeyDispatcherState() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean dispatchKeyEventPreIme(android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean dispatchKeyEvent(android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean dispatchKeyShortcutEvent(android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean dispatchTouchEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onFilterTouchEventForSecurity(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean dispatchTrackballEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean dispatchGenericMotionEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\n    protected  boolean dispatchHoverEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\n    protected  boolean dispatchGenericPointerEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\n    protected  boolean dispatchGenericFocusedEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  void dispatchWindowFocusChanged(boolean hasFocus) { throw new RuntimeException(\"Stub!\"); }\n    public  void onWindowFocusChanged(boolean hasWindowFocus) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean hasWindowFocus() { throw new RuntimeException(\"Stub!\"); }\n    protected  void dispatchVisibilityChanged(android.view.View changedView, int visibility) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onVisibilityChanged(android.view.View changedView, int visibility) { throw new RuntimeException(\"Stub!\"); }\n    public  void dispatchDisplayHint(int hint) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onDisplayHint(int hint) { throw new RuntimeException(\"Stub!\"); }\n    public  void dispatchWindowVisibilityChanged(int visibility) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onWindowVisibilityChanged(int visibility) { throw new RuntimeException(\"Stub!\"); }\n    public  int getWindowVisibility() { throw new RuntimeException(\"Stub!\"); }\n    public  void getWindowVisibleDisplayFrame(android.graphics.Rect outRect) { throw new RuntimeException(\"Stub!\"); }\n    public  void dispatchConfigurationChanged(android.content.res.Configuration newConfig) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onConfigurationChanged(android.content.res.Configuration newConfig) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty()\n    public  boolean isInTouchMode() { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.CapturedViewProperty()\n    public final  android.content.Context getContext() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onKeyPreIme(int keyCode, android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onKeyDown(int keyCode, android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onKeyLongPress(int keyCode, android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onKeyUp(int keyCode, android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onKeyMultiple(int keyCode, int repeatCount, android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onKeyShortcut(int keyCode, android.view.KeyEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onCheckIsTextEditor() { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.EditorInfo outAttrs) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean checkInputConnectionProxy(android.view.View view) { throw new RuntimeException(\"Stub!\"); }\n    public  void createContextMenu(android.view.ContextMenu menu) { throw new RuntimeException(\"Stub!\"); }\n    protected  android.view.ContextMenu.ContextMenuInfo getContextMenuInfo() { throw new RuntimeException(\"Stub!\"); }\n    protected  void onCreateContextMenu(android.view.ContextMenu menu) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onTrackballEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onGenericMotionEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onHoverEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty()\n    public  boolean isHovered() { throw new RuntimeException(\"Stub!\"); }\n    public  void setHovered(boolean hovered) { throw new RuntimeException(\"Stub!\"); }\n    public  void onHoverChanged(boolean hovered) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onTouchEvent(android.view.MotionEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  void cancelLongPress() { throw new RuntimeException(\"Stub!\"); }\n    public  void setTouchDelegate(android.view.TouchDelegate delegate) { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.TouchDelegate getTouchDelegate() { throw new RuntimeException(\"Stub!\"); }\n    public  void bringToFront() { throw new RuntimeException(\"Stub!\"); }\n    protected  void onScrollChanged(int l, int t, int oldl, int oldt) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onSizeChanged(int w, int h, int oldw, int oldh) { throw new RuntimeException(\"Stub!\"); }\n    protected  void dispatchDraw(android.graphics.Canvas canvas) { throw new RuntimeException(\"Stub!\"); }\n    public final  android.view.ViewParent getParent() { throw new RuntimeException(\"Stub!\"); }\n    public  void setScrollX(int value) { throw new RuntimeException(\"Stub!\"); }\n    public  void setScrollY(int value) { throw new RuntimeException(\"Stub!\"); }\n    public final  int getScrollX() { throw new RuntimeException(\"Stub!\"); }\n    public final  int getScrollY() { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"layout\")\n    public final  int getWidth() { return 0; }\n    @android.view.ViewDebug.ExportedProperty(category=\"layout\")\n    public final  int getHeight() { return 0; }\n    public  void getDrawingRect(android.graphics.Rect outRect) { throw new RuntimeException(\"Stub!\"); }\n    public final  int getMeasuredWidth() { throw new RuntimeException(\"Stub!\"); }\n    public final  int getMeasuredWidthAndState() { throw new RuntimeException(\"Stub!\"); }\n    public final  int getMeasuredHeight() { throw new RuntimeException(\"Stub!\"); }\n    public final  int getMeasuredHeightAndState() { throw new RuntimeException(\"Stub!\"); }\n    public final  int getMeasuredState() { throw new RuntimeException(\"Stub!\"); }\n    public  android.graphics.Matrix getMatrix() { throw new RuntimeException(\"Stub!\"); }\n    public  float getCameraDistance() { throw new RuntimeException(\"Stub!\"); }\n    public  void setCameraDistance(float distance) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  float getRotation() { throw new RuntimeException(\"Stub!\"); }\n    public  void setRotation(float rotation) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  float getRotationY() { throw new RuntimeException(\"Stub!\"); }\n    public  void setRotationY(float rotationY) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  float getRotationX() { throw new RuntimeException(\"Stub!\"); }\n    public  void setRotationX(float rotationX) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  float getScaleX() { throw new RuntimeException(\"Stub!\"); }\n    public  void setScaleX(float scaleX) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  float getScaleY() { throw new RuntimeException(\"Stub!\"); }\n    public  void setScaleY(float scaleY) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  float getPivotX() { throw new RuntimeException(\"Stub!\"); }\n    public  void setPivotX(float pivotX) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  float getPivotY() { throw new RuntimeException(\"Stub!\"); }\n    public  void setPivotY(float pivotY) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  float getAlpha() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean hasOverlappingRendering() { throw new RuntimeException(\"Stub!\"); }\n    public  void setAlpha(float alpha) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.CapturedViewProperty()\n    public final  int getTop() { throw new RuntimeException(\"Stub!\"); }\n    public final  void setTop(int top) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.CapturedViewProperty()\n    public final  int getBottom() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isDirty() { throw new RuntimeException(\"Stub!\"); }\n    public final  void setBottom(int bottom) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.CapturedViewProperty()\n    public final  int getLeft() { throw new RuntimeException(\"Stub!\"); }\n    public final  void setLeft(int left) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.CapturedViewProperty()\n    public final  int getRight() { throw new RuntimeException(\"Stub!\"); }\n    public final  void setRight(int right) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  float getX() { throw new RuntimeException(\"Stub!\"); }\n    public  void setX(float x) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  float getY() { throw new RuntimeException(\"Stub!\"); }\n    public  void setY(float y) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  float getTranslationX() { throw new RuntimeException(\"Stub!\"); }\n    public  void setTranslationX(float translationX) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  float getTranslationY() { throw new RuntimeException(\"Stub!\"); }\n    public  void setTranslationY(float translationY) { throw new RuntimeException(\"Stub!\"); }\n    public  void getHitRect(android.graphics.Rect outRect) { throw new RuntimeException(\"Stub!\"); }\n    public  void getFocusedRect(android.graphics.Rect r) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean getGlobalVisibleRect(android.graphics.Rect r, android.graphics.Point globalOffset) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean getGlobalVisibleRect(android.graphics.Rect r) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean getLocalVisibleRect(android.graphics.Rect r) { throw new RuntimeException(\"Stub!\"); }\n    public  void offsetTopAndBottom(int offset) { throw new RuntimeException(\"Stub!\"); }\n    public  void offsetLeftAndRight(int offset) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(deepExport=true,prefix=\"layout_\")\n    public  android.view.ViewGroup.LayoutParams getLayoutParams() { throw new RuntimeException(\"Stub!\"); }\n    public  void setLayoutParams(android.view.ViewGroup.LayoutParams params) { throw new RuntimeException(\"Stub!\"); }\n    public  void scrollTo(int x, int y) { throw new RuntimeException(\"Stub!\"); }\n    public  void scrollBy(int x, int y) { throw new RuntimeException(\"Stub!\"); }\n    protected  boolean awakenScrollBars() { throw new RuntimeException(\"Stub!\"); }\n    protected  boolean awakenScrollBars(int startDelay) { throw new RuntimeException(\"Stub!\"); }\n    protected  boolean awakenScrollBars(int startDelay, boolean invalidate) { throw new RuntimeException(\"Stub!\"); }\n    public  void invalidate(android.graphics.Rect dirty) { throw new RuntimeException(\"Stub!\"); }\n    public  void invalidate(int l, int t, int r, int b) { throw new RuntimeException(\"Stub!\"); }\n    public  void invalidate() { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  boolean isOpaque() { throw new RuntimeException(\"Stub!\"); }\n    public  android.os.Handler getHandler() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean post(java.lang.Runnable action) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean postDelayed(java.lang.Runnable action, long delayMillis) { throw new RuntimeException(\"Stub!\"); }\n    public  void postOnAnimation(java.lang.Runnable action) { throw new RuntimeException(\"Stub!\"); }\n    public  void postOnAnimationDelayed(java.lang.Runnable action, long delayMillis) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean removeCallbacks(java.lang.Runnable action) { throw new RuntimeException(\"Stub!\"); }\n    public  void postInvalidate() { throw new RuntimeException(\"Stub!\"); }\n    public  void postInvalidate(int left, int top, int right, int bottom) { throw new RuntimeException(\"Stub!\"); }\n    public  void postInvalidateDelayed(long delayMilliseconds) { throw new RuntimeException(\"Stub!\"); }\n    public  void postInvalidateDelayed(long delayMilliseconds, int left, int top, int right, int bottom) { throw new RuntimeException(\"Stub!\"); }\n    public  void postInvalidateOnAnimation() { throw new RuntimeException(\"Stub!\"); }\n    public  void postInvalidateOnAnimation(int left, int top, int right, int bottom) { throw new RuntimeException(\"Stub!\"); }\n    public  void computeScroll() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isHorizontalFadingEdgeEnabled() { throw new RuntimeException(\"Stub!\"); }\n    public  void setHorizontalFadingEdgeEnabled(boolean horizontalFadingEdgeEnabled) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isVerticalFadingEdgeEnabled() { throw new RuntimeException(\"Stub!\"); }\n    public  void setVerticalFadingEdgeEnabled(boolean verticalFadingEdgeEnabled) { throw new RuntimeException(\"Stub!\"); }\n    protected  float getTopFadingEdgeStrength() { throw new RuntimeException(\"Stub!\"); }\n    protected  float getBottomFadingEdgeStrength() { throw new RuntimeException(\"Stub!\"); }\n    protected  float getLeftFadingEdgeStrength() { throw new RuntimeException(\"Stub!\"); }\n    protected  float getRightFadingEdgeStrength() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isHorizontalScrollBarEnabled() { throw new RuntimeException(\"Stub!\"); }\n    public  void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isVerticalScrollBarEnabled() { throw new RuntimeException(\"Stub!\"); }\n    public  void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) { throw new RuntimeException(\"Stub!\"); }\n    public  void setScrollbarFadingEnabled(boolean fadeScrollbars) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isScrollbarFadingEnabled() { throw new RuntimeException(\"Stub!\"); }\n    public  int getScrollBarDefaultDelayBeforeFade() { throw new RuntimeException(\"Stub!\"); }\n    public  void setScrollBarDefaultDelayBeforeFade(int scrollBarDefaultDelayBeforeFade) { throw new RuntimeException(\"Stub!\"); }\n    public  int getScrollBarFadeDuration() { throw new RuntimeException(\"Stub!\"); }\n    public  void setScrollBarFadeDuration(int scrollBarFadeDuration) { throw new RuntimeException(\"Stub!\"); }\n    public  int getScrollBarSize() { throw new RuntimeException(\"Stub!\"); }\n    public  void setScrollBarSize(int scrollBarSize) { throw new RuntimeException(\"Stub!\"); }\n    public  void setScrollBarStyle(int style) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=0,to=\"INSIDE_OVERLAY\"),@android.view.ViewDebug.IntToString(from=16777216,to=\"INSIDE_INSET\"),@android.view.ViewDebug.IntToString(from=33554432,to=\"OUTSIDE_OVERLAY\"),@android.view.ViewDebug.IntToString(from=50331648,to=\"OUTSIDE_INSET\")})\n    public  int getScrollBarStyle() { throw new RuntimeException(\"Stub!\"); }\n    protected  int computeHorizontalScrollRange() { throw new RuntimeException(\"Stub!\"); }\n    protected  int computeHorizontalScrollOffset() { throw new RuntimeException(\"Stub!\"); }\n    protected  int computeHorizontalScrollExtent() { throw new RuntimeException(\"Stub!\"); }\n    protected  int computeVerticalScrollRange() { throw new RuntimeException(\"Stub!\"); }\n    protected  int computeVerticalScrollOffset() { throw new RuntimeException(\"Stub!\"); }\n    protected  int computeVerticalScrollExtent() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean canScrollHorizontally(int direction) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean canScrollVertically(int direction) { throw new RuntimeException(\"Stub!\"); }\n    protected final  void onDrawScrollBars(android.graphics.Canvas canvas) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onDraw(android.graphics.Canvas canvas) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onAttachedToWindow() { throw new RuntimeException(\"Stub!\"); }\n    public  void onScreenStateChanged(int screenState) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onDetachedFromWindow() { throw new RuntimeException(\"Stub!\"); }\n    protected  int getWindowAttachCount() { throw new RuntimeException(\"Stub!\"); }\n    public  android.os.IBinder getWindowToken() { throw new RuntimeException(\"Stub!\"); }\n    public  android.os.IBinder getApplicationWindowToken() { throw new RuntimeException(\"Stub!\"); }\n    public  void saveHierarchyState(android.util.SparseArray<android.os.Parcelable> container) { throw new RuntimeException(\"Stub!\"); }\n    protected  void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable> container) { throw new RuntimeException(\"Stub!\"); }\n    protected  android.os.Parcelable onSaveInstanceState() { throw new RuntimeException(\"Stub!\"); }\n    public  void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable> container) { throw new RuntimeException(\"Stub!\"); }\n    protected  void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable> container) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onRestoreInstanceState(android.os.Parcelable state) { throw new RuntimeException(\"Stub!\"); }\n    public  long getDrawingTime() { throw new RuntimeException(\"Stub!\"); }\n    public  void setDuplicateParentStateEnabled(boolean enabled) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isDuplicateParentStateEnabled() { throw new RuntimeException(\"Stub!\"); }\n    public  void setLayerType(int layerType, android.graphics.Paint paint) { throw new RuntimeException(\"Stub!\"); }\n    public  int getLayerType() { throw new RuntimeException(\"Stub!\"); }\n    public  void buildLayer() { throw new RuntimeException(\"Stub!\"); }\n    public  void setDrawingCacheEnabled(boolean enabled) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  boolean isDrawingCacheEnabled() { throw new RuntimeException(\"Stub!\"); }\n    public  android.graphics.Bitmap getDrawingCache() { throw new RuntimeException(\"Stub!\"); }\n    public  android.graphics.Bitmap getDrawingCache(boolean autoScale) { throw new RuntimeException(\"Stub!\"); }\n    public  void destroyDrawingCache() { throw new RuntimeException(\"Stub!\"); }\n    public  void setDrawingCacheBackgroundColor(int color) { throw new RuntimeException(\"Stub!\"); }\n    public  int getDrawingCacheBackgroundColor() { throw new RuntimeException(\"Stub!\"); }\n    public  void buildDrawingCache() { throw new RuntimeException(\"Stub!\"); }\n    public  void buildDrawingCache(boolean autoScale) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isInEditMode() { throw new RuntimeException(\"Stub!\"); }\n    protected  boolean isPaddingOffsetRequired() { throw new RuntimeException(\"Stub!\"); }\n    protected  int getLeftPaddingOffset() { throw new RuntimeException(\"Stub!\"); }\n    protected  int getRightPaddingOffset() { throw new RuntimeException(\"Stub!\"); }\n    protected  int getTopPaddingOffset() { throw new RuntimeException(\"Stub!\"); }\n    protected  int getBottomPaddingOffset() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isHardwareAccelerated() { throw new RuntimeException(\"Stub!\"); }\n    public  void draw(android.graphics.Canvas canvas) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"drawing\")\n    public  int getSolidColor() { throw new RuntimeException(\"Stub!\"); }\n    public  boolean isLayoutRequested() { throw new RuntimeException(\"Stub!\"); }\n    @java.lang.SuppressWarnings(value={\"unchecked\"})\n    public  void layout(int l, int t, int r, int b) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onLayout(boolean changed, int left, int top, int right, int bottom) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onFinishInflate() { throw new RuntimeException(\"Stub!\"); }\n    public  android.content.res.Resources getResources() { throw new RuntimeException(\"Stub!\"); }\n    public  void invalidateDrawable(android.graphics.drawable.Drawable drawable) { throw new RuntimeException(\"Stub!\"); }\n    public  void scheduleDrawable(android.graphics.drawable.Drawable who, java.lang.Runnable what, long when) { throw new RuntimeException(\"Stub!\"); }\n    public  void unscheduleDrawable(android.graphics.drawable.Drawable who, java.lang.Runnable what) { throw new RuntimeException(\"Stub!\"); }\n    public  void unscheduleDrawable(android.graphics.drawable.Drawable who) { throw new RuntimeException(\"Stub!\"); }\n    protected  boolean verifyDrawable(android.graphics.drawable.Drawable who) { throw new RuntimeException(\"Stub!\"); }\n    protected  void drawableStateChanged() { throw new RuntimeException(\"Stub!\"); }\n    public  void refreshDrawableState() { throw new RuntimeException(\"Stub!\"); }\n    public final  int[] getDrawableState() { throw new RuntimeException(\"Stub!\"); }\n    protected  int[] onCreateDrawableState(int extraSpace) { throw new RuntimeException(\"Stub!\"); }\n    protected static  int[] mergeDrawableStates(int[] baseState, int[] additionalState) { throw new RuntimeException(\"Stub!\"); }\n    public  void jumpDrawablesToCurrentState() { throw new RuntimeException(\"Stub!\"); }\n    public  void setBackgroundColor(int color) { throw new RuntimeException(\"Stub!\"); }\n    public  void setBackgroundResource(int resid) { throw new RuntimeException(\"Stub!\"); }\n    public  void setBackground(android.graphics.drawable.Drawable background) { throw new RuntimeException(\"Stub!\"); }\n    @java.lang.Deprecated()\n    public  void setBackgroundDrawable(android.graphics.drawable.Drawable background) { throw new RuntimeException(\"Stub!\"); }\n    public  android.graphics.drawable.Drawable getBackground() { throw new RuntimeException(\"Stub!\"); }\n    public  void setPadding(int left, int top, int right, int bottom) { throw new RuntimeException(\"Stub!\"); }\n    public  int getPaddingTop() { throw new RuntimeException(\"Stub!\"); }\n    public  int getPaddingBottom() { throw new RuntimeException(\"Stub!\"); }\n    public  int getPaddingLeft() { throw new RuntimeException(\"Stub!\"); }\n    public  int getPaddingRight() { throw new RuntimeException(\"Stub!\"); }\n    public  void setSelected(boolean selected) { throw new RuntimeException(\"Stub!\"); }\n    protected  void dispatchSetSelected(boolean selected) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty()\n    public  boolean isSelected() { throw new RuntimeException(\"Stub!\"); }\n    public  void setActivated(boolean activated) { throw new RuntimeException(\"Stub!\"); }\n    protected  void dispatchSetActivated(boolean activated) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty()\n    public  boolean isActivated() { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.ViewTreeObserver getViewTreeObserver() { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.View getRootView() { throw new RuntimeException(\"Stub!\"); }\n    public  void getLocationOnScreen(int[] location) { throw new RuntimeException(\"Stub!\"); }\n    public  void getLocationInWindow(int[] location) { throw new RuntimeException(\"Stub!\"); }\n    public final  android.view.View findViewById(int id) { throw new RuntimeException(\"Stub!\"); }\n    public final  android.view.View findViewWithTag(java.lang.Object tag) { throw new RuntimeException(\"Stub!\"); }\n    public  void setId(int id) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.CapturedViewProperty()\n    public  int getId() { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty()\n    public  java.lang.Object getTag() { throw new RuntimeException(\"Stub!\"); }\n    public  void setTag(java.lang.Object tag) { throw new RuntimeException(\"Stub!\"); }\n    public  java.lang.Object getTag(int key) { throw new RuntimeException(\"Stub!\"); }\n    public  void setTag(int key, java.lang.Object tag) { throw new RuntimeException(\"Stub!\"); }\n    @android.view.ViewDebug.ExportedProperty(category=\"layout\")\n    public  int getBaseline() { throw new RuntimeException(\"Stub!\"); }\n    public  void requestLayout() { throw new RuntimeException(\"Stub!\"); }\n    public  void forceLayout() { throw new RuntimeException(\"Stub!\"); }\n    public final  void measure(int widthMeasureSpec, int heightMeasureSpec) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { throw new RuntimeException(\"Stub!\"); }\n    protected final  void setMeasuredDimension(int measuredWidth, int measuredHeight) { throw new RuntimeException(\"Stub!\"); }\n    public static  int combineMeasuredStates(int curState, int newState) { throw new RuntimeException(\"Stub!\"); }\n    public static  int resolveSize(int size, int measureSpec) { throw new RuntimeException(\"Stub!\"); }\n    public static  int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { throw new RuntimeException(\"Stub!\"); }\n    public static  int getDefaultSize(int size, int measureSpec) { throw new RuntimeException(\"Stub!\"); }\n    protected  int getSuggestedMinimumHeight() { throw new RuntimeException(\"Stub!\"); }\n    protected  int getSuggestedMinimumWidth() { throw new RuntimeException(\"Stub!\"); }\n    public  int getMinimumHeight() { throw new RuntimeException(\"Stub!\"); }\n    public  void setMinimumHeight(int minHeight) { throw new RuntimeException(\"Stub!\"); }\n    public  int getMinimumWidth() { throw new RuntimeException(\"Stub!\"); }\n    public  void setMinimumWidth(int minWidth) { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.animation.Animation getAnimation() { throw new RuntimeException(\"Stub!\"); }\n    public  void startAnimation(android.view.animation.Animation animation) { throw new RuntimeException(\"Stub!\"); }\n    public  void clearAnimation() { throw new RuntimeException(\"Stub!\"); }\n    public  void setAnimation(android.view.animation.Animation animation) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onAnimationStart() { throw new RuntimeException(\"Stub!\"); }\n    protected  void onAnimationEnd() { throw new RuntimeException(\"Stub!\"); }\n    protected  boolean onSetAlpha(int alpha) { throw new RuntimeException(\"Stub!\"); }\n    public  void playSoundEffect(int soundConstant) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean performHapticFeedback(int feedbackConstant) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean performHapticFeedback(int feedbackConstant, int flags) { throw new RuntimeException(\"Stub!\"); }\n    public  void setSystemUiVisibility(int visibility) { throw new RuntimeException(\"Stub!\"); }\n    public  int getSystemUiVisibility() { throw new RuntimeException(\"Stub!\"); }\n    public  int getWindowSystemUiVisibility() { throw new RuntimeException(\"Stub!\"); }\n    public  void onWindowSystemUiVisibilityChanged(int visible) { throw new RuntimeException(\"Stub!\"); }\n    public  void dispatchWindowSystemUiVisiblityChanged(int visible) { throw new RuntimeException(\"Stub!\"); }\n    public  void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener l) { throw new RuntimeException(\"Stub!\"); }\n    public  void dispatchSystemUiVisibilityChanged(int visibility) { throw new RuntimeException(\"Stub!\"); }\n    public final  boolean startDrag(android.content.ClipData data, android.view.View.DragShadowBuilder shadowBuilder, java.lang.Object myLocalState, int flags) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean onDragEvent(android.view.DragEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public  boolean dispatchDragEvent(android.view.DragEvent event) { throw new RuntimeException(\"Stub!\"); }\n    public static  android.view.View inflate(android.content.Context context, int resource, android.view.ViewGroup root) { throw new RuntimeException(\"Stub!\"); }\n    @java.lang.SuppressWarnings(value={\"UnusedParameters\"})\n    protected  boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { throw new RuntimeException(\"Stub!\"); }\n    protected  void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { throw new RuntimeException(\"Stub!\"); }\n    public  int getOverScrollMode() { throw new RuntimeException(\"Stub!\"); }\n    public  void setOverScrollMode(int overScrollMode) { throw new RuntimeException(\"Stub!\"); }\n    public  android.view.ViewPropertyAnimator animate() { throw new RuntimeException(\"Stub!\"); }\n    protected static final java.lang.String VIEW_LOG_TAG = \"View\";\n    public static final int NO_ID = -1;\n    public static final int VISIBLE = 0;\n    public static final int INVISIBLE = 4;\n    public static final int GONE = 8;\n    public static final int DRAWING_CACHE_QUALITY_LOW = 524288;\n    public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576;\n    public static final int DRAWING_CACHE_QUALITY_AUTO = 0;\n    public static final int SCROLLBARS_INSIDE_OVERLAY = 0;\n    public static final int SCROLLBARS_INSIDE_INSET = 16777216;\n    public static final int SCROLLBARS_OUTSIDE_OVERLAY = 33554432;\n    public static final int SCROLLBARS_OUTSIDE_INSET = 50331648;\n    public static final int KEEP_SCREEN_ON = 67108864;\n    public static final int SOUND_EFFECTS_ENABLED = 134217728;\n    public static final int HAPTIC_FEEDBACK_ENABLED = 268435456;\n    public static final int FOCUSABLES_ALL = 0;\n    public static final int FOCUSABLES_TOUCH_MODE = 1;\n    public static final int FOCUS_BACKWARD = 1;\n    public static final int FOCUS_FORWARD = 2;\n    public static final int FOCUS_LEFT = 17;\n    public static final int FOCUS_UP = 33;\n    public static final int FOCUS_RIGHT = 66;\n    public static final int FOCUS_DOWN = 130;\n    public static final int MEASURED_SIZE_MASK = 16777215;\n    public static final int MEASURED_STATE_MASK = -16777216;\n    public static final int MEASURED_HEIGHT_STATE_SHIFT = 16;\n    public static final int MEASURED_STATE_TOO_SMALL = 16777216;\n    protected static final int[] EMPTY_STATE_SET = null;\n    protected static final int[] ENABLED_STATE_SET = null;\n    protected static final int[] FOCUSED_STATE_SET = null;\n    protected static final int[] SELECTED_STATE_SET = null;\n    protected static final int[] WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] ENABLED_FOCUSED_STATE_SET = null;\n    protected static final int[] ENABLED_SELECTED_STATE_SET = null;\n    protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] FOCUSED_SELECTED_STATE_SET = null;\n    protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] SELECTED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET = null;\n    protected static final int[] ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] PRESSED_SELECTED_STATE_SET = null;\n    protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] PRESSED_FOCUSED_STATE_SET = null;\n    protected static final int[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] PRESSED_FOCUSED_SELECTED_STATE_SET = null;\n    protected static final int[] PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] PRESSED_ENABLED_STATE_SET = null;\n    protected static final int[] PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] PRESSED_ENABLED_SELECTED_STATE_SET = null;\n    protected static final int[] PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] PRESSED_ENABLED_FOCUSED_STATE_SET = null;\n    protected static final int[] PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = null;\n    protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET = null;\n    protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = null;\n    public static final int TEXT_ALIGNMENT_INHERIT = 0;\n    public static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT = 131072;\n    public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0;\n    public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1;\n    public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2;\n    public static final int OVER_SCROLL_ALWAYS = 0;\n    public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1;\n    public static final int OVER_SCROLL_NEVER = 2;\n    public static final int SYSTEM_UI_FLAG_VISIBLE = 0;\n    public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1;\n    public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2;\n    public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4;\n    public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256;\n    public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512;\n    public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024;\n    @Deprecated\n    public static final int STATUS_BAR_HIDDEN = 1;\n    @Deprecated\n    public static final int STATUS_BAR_VISIBLE = 0;\n    public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536;\n    public static final int FIND_VIEWS_WITH_TEXT = 1;\n    public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 2;\n    public static final int SCREEN_STATE_OFF = 0;\n    public static final int SCREEN_STATE_ON = 1;\n    public static final int SCROLLBAR_POSITION_DEFAULT = 0;\n    public static final int SCROLLBAR_POSITION_LEFT = 1;\n    public static final int SCROLLBAR_POSITION_RIGHT = 2;\n    public static final int LAYER_TYPE_NONE = 0;\n    public static final int LAYER_TYPE_SOFTWARE = 1;\n    public static final int LAYER_TYPE_HARDWARE = 2;\n    public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;\n    public static final android.util.Property<android.view.View, java.lang.Float> TRANSLATION_X;\n    public static final android.util.Property<android.view.View, java.lang.Float> TRANSLATION_Y;\n    public static final android.util.Property<android.view.View, java.lang.Float> X;\n    public static final android.util.Property<android.view.View, java.lang.Float> Y;\n    public static final android.util.Property<android.view.View, java.lang.Float> ROTATION;\n    public static final android.util.Property<android.view.View, java.lang.Float> ROTATION_X;\n    public static final android.util.Property<android.view.View, java.lang.Float> ROTATION_Y;\n    public static final android.util.Property<android.view.View, java.lang.Float> SCALE_X;\n    public static final android.util.Property<android.view.View, java.lang.Float> SCALE_Y;\n    static { ALPHA = null; TRANSLATION_X = null; TRANSLATION_Y = null; X = null; Y = null; ROTATION = null; ROTATION_X = null; ROTATION_Y = null; SCALE_X = null; SCALE_Y = null; }\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Api.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.{NineCardsCategory, NineCardsMoment}\n\ncase class CategorizedDetailPackage(\n    packageName: String,\n    title: String,\n    category: Option[NineCardsCategory],\n    icon: String,\n    free: Boolean,\n    downloads: String,\n    stars: Double)\n\ncase class CategorizedPackage(packageName: String, category: Option[NineCardsCategory])\n\ncase class LoginResponse(apiKey: String, sessionToken: String)\n\ncase class RankApps(category: NineCardsCategory, packages: Seq[String])\n\ncase class RankAppsByMoment(moment: NineCardsMoment, packages: Seq[String])\n\ncase class RankWidget(packageName: String, className: String)\n\ncase class RankWidgetsByMoment(moment: NineCardsMoment, widgets: Seq[RankWidget])\n\ncase class NotCategorizedPackage(\n    packageName: String,\n    title: String,\n    icon: Option[String],\n    downloads: String,\n    stars: Double,\n    free: Boolean,\n    screenshots: Seq[String])\n\ncase class RequestConfig(\n    apiKey: String,\n    sessionToken: String,\n    androidId: String,\n    marketToken: Option[String] = None)\n\ncase class RequestConfigV1(deviceId: String, token: String, marketToken: Option[String])\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/ApiV1.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.{CardType, CollectionType, NineCardsCategory}\nimport play.api.libs.json._\n\ncase class Device(name: String, deviceId: String, secretToken: String, permissions: Seq[String])\n\ncase class LoginResponseV1(\n    userId: Option[String],\n    sessionToken: Option[String],\n    email: Option[String],\n    devices: Seq[Device])\n\ncase class UserV1(\n    _id: String,\n    email: String,\n    plusProfile: UserV1PlusProfile,\n    devices: Seq[UserV1Device],\n    status: UserV1StatusInfo)\n\ncase class UserV1Collection(\n    name: String,\n    originalSharedCollectionId: Option[String],\n    sharedCollectionId: Option[String],\n    sharedCollectionSubscribed: Option[Boolean],\n    items: Seq[UserV1CollectionItem],\n    collectionType: CollectionType,\n    constrains: Seq[String],\n    wifi: Seq[String],\n    occurrence: Seq[String],\n    icon: String,\n    category: Option[NineCardsCategory])\n\ncase class UserV1CollectionItem(\n    itemType: CardType,\n    title: String,\n    intent: String,\n    categories: Option[Seq[NineCardsCategory]])\n\ncase class UserV1Device(deviceId: String, deviceName: String, collections: Seq[UserV1Collection])\n\ncase class UserV1PlusProfile(displayName: String, profileImage: UserV1ProfileImage)\n\ncase class UserV1ProfileImage(imageType: Int, imageUrl: String)\n\ncase class UserV1StatusInfo(\n    products: Seq[String],\n    friendsReferred: Int,\n    themesShared: Int,\n    collectionsShared: Int,\n    customCollections: Int,\n    earlyAdopter: Boolean,\n    communityMember: Boolean,\n    joinedThrough: Option[String],\n    tester: Boolean)\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Application.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.{AppCardType, NineCardsCategory}\n\ncase class Application(\n    id: Int,\n    name: String,\n    packageName: String,\n    className: String,\n    category: NineCardsCategory,\n    dateInstalled: Long,\n    dateUpdated: Long,\n    version: String,\n    installedFromGooglePlay: Boolean)\n\ncase class ApplicationData(\n    name: String,\n    packageName: String,\n    className: String,\n    category: NineCardsCategory,\n    dateInstalled: Long,\n    dateUpdated: Long,\n    version: String,\n    installedFromGooglePlay: Boolean)\n\nobject Application extends NineCardsIntentConversions {\n\n  implicit class ApplicationOps(app: Application) {\n\n    def toData =\n      ApplicationData(\n        name = app.name,\n        packageName = app.packageName,\n        className = app.className,\n        category = app.category,\n        dateInstalled = app.dateInstalled,\n        dateUpdated = app.dateUpdated,\n        version = app.version,\n        installedFromGooglePlay = app.installedFromGooglePlay)\n  }\n\n  implicit class ApplicationDataOps(app: ApplicationData) {\n\n    def toApp(id: Int) =\n      Application(\n        id = id,\n        name = app.name,\n        packageName = app.packageName,\n        className = app.className,\n        category = app.category,\n        dateInstalled = app.dateInstalled,\n        dateUpdated = app.dateUpdated,\n        version = app.version,\n        installedFromGooglePlay = app.installedFromGooglePlay)\n\n    def toCardData: CardData =\n      CardData(\n        term = app.name,\n        packageName = Some(app.packageName),\n        cardType = AppCardType,\n        intent = toNineCardIntent(app),\n        imagePath = None)\n  }\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Awareness.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.{ConditionWeather, KindActivity}\n\ncase class ProbablyActivity(activityType: KindActivity)\n\ncase class Headphones(connected: Boolean)\n\ncase class WeatherState(\n    conditions: Seq[ConditionWeather],\n    humidity: Int,\n    dewPointCelsius: Float,\n    dewPointFahrenheit: Float,\n    temperatureCelsius: Float,\n    temperatureFahrenheit: Float)\n\ncase class LocationState(\n    accuracy: Float,\n    altitude: Double,\n    bearing: Float,\n    latitude: Double,\n    longitude: Double,\n    speed: Float,\n    elapsedTime: Long,\n    time: Long)\n\ncase class Location(\n    latitude: Double,\n    longitude: Double,\n    countryCode: Option[String],\n    countryName: Option[String],\n    addressLines: Seq[String])\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/BitmapPath.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\ncase class BitmapPath(name: String, path: String)\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Call.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.{CallType, PhoneCategory}\n\ncase class Call(\n    number: String,\n    name: Option[String] = None,\n    numberType: PhoneCategory,\n    date: Long,\n    callType: CallType)\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Card.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.CardType\n\ncase class Card(\n    id: Int,\n    position: Int,\n    term: String,\n    packageName: Option[String],\n    cardType: CardType,\n    intent: NineCardsIntent,\n    imagePath: Option[String],\n    notification: Option[String] = None)\n    extends Serializable\n\ncase class CardData(\n    position: Int = 0,\n    term: String,\n    packageName: Option[String],\n    cardType: CardType,\n    intent: NineCardsIntent,\n    imagePath: Option[String] = None,\n    notification: Option[String] = None)\n    extends Serializable\n\nobject Card {\n\n  implicit class CardOps(card: Card) {\n\n    def toData =\n      CardData(\n        position = card.position,\n        term = card.term,\n        packageName = card.packageName,\n        cardType = card.cardType,\n        intent = card.intent,\n        imagePath = card.imagePath,\n        notification = card.notification)\n  }\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/CloudStorage.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport java.util.Date\n\nimport cards.nine.models.types._\n\ntrait CloudStorageResource {\n  def cloudId: String\n  def deviceId: Option[String]\n  def deviceName: String\n  def createdDate: Date\n  def modifiedDate: Date\n}\n\ncase class CloudStorageDeviceSummary(\n    cloudId: String,\n    deviceId: Option[String],\n    deviceName: String,\n    createdDate: Date,\n    modifiedDate: Date,\n    currentDevice: Boolean)\n    extends CloudStorageResource\n\ncase class CloudStorageDevice(\n    cloudId: String,\n    createdDate: Date,\n    modifiedDate: Date,\n    data: CloudStorageDeviceData)\n    extends CloudStorageResource {\n\n  override def deviceId: Option[String] = Some(data.deviceId)\n\n  override def deviceName: String = data.deviceName\n}\n\ncase class RawCloudStorageDevice(\n    cloudId: String,\n    uuid: String,\n    title: String,\n    deviceId: Option[String],\n    createdDate: Date,\n    modifiedDate: Date,\n    json: String)\n\ncase class CloudStorageDeviceData(\n    deviceId: String,\n    deviceName: String,\n    documentVersion: Int,\n    collections: Seq[CloudStorageCollection],\n    moments: Option[Seq[CloudStorageMoment]],\n    dockApps: Option[Seq[CloudStorageDockApp]])\n\ncase class CloudStorageCollection(\n    name: String,\n    originalSharedCollectionId: Option[String],\n    sharedCollectionId: Option[String],\n    sharedCollectionSubscribed: Option[Boolean],\n    items: Seq[CloudStorageCollectionItem],\n    collectionType: CollectionType,\n    icon: String,\n    category: Option[NineCardsCategory],\n    moment: Option[CloudStorageMoment])\n\ncase class CloudStorageCollectionItem(itemType: String, title: String, intent: String)\n\ncase class CloudStorageMoment(\n    timeslot: Seq[CloudStorageMomentTimeSlot],\n    wifi: Seq[String],\n    bluetooth: Option[Seq[String]],\n    headphones: Boolean,\n    momentType: NineCardsMoment,\n    widgets: Option[Seq[CloudStorageWidget]])\n\ncase class CloudStorageMomentTimeSlot(from: String, to: String, days: Seq[Int])\n\ncase class CloudStorageWidget(\n    packageName: String,\n    className: String,\n    area: CloudStorageWidgetArea,\n    widgetType: WidgetType,\n    label: Option[String],\n    imagePath: Option[String],\n    intent: Option[String])\n\ncase class CloudStorageWidgetArea(startX: Int, startY: Int, spanX: Int, spanY: Int)\n\ncase class CloudStorageDockApp(\n    name: String,\n    dockType: DockType,\n    intent: String,\n    imagePath: String,\n    position: Int)\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Collection.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types._\n\ncase class Collection(\n    id: Int,\n    position: Int,\n    name: String,\n    collectionType: CollectionType,\n    icon: String,\n    themedColorIndex: Int,\n    appsCategory: Option[NineCardsCategory] = None,\n    cards: Seq[Card] = Seq.empty,\n    moment: Option[Moment] = None,\n    originalSharedCollectionId: Option[String] = None,\n    sharedCollectionId: Option[String] = None,\n    sharedCollectionSubscribed: Boolean,\n    publicCollectionStatus: PublicCollectionStatus)\n    extends Serializable\n\ncase class CollectionData(\n    position: Int = 0,\n    name: String,\n    collectionType: CollectionType,\n    icon: String,\n    themedColorIndex: Int,\n    appsCategory: Option[NineCardsCategory] = None,\n    cards: Seq[CardData] = Seq.empty,\n    moment: Option[MomentData] = None,\n    originalSharedCollectionId: Option[String] = None,\n    sharedCollectionId: Option[String] = None,\n    sharedCollectionSubscribed: Boolean = false,\n    publicCollectionStatus: PublicCollectionStatus = NotPublished)\n    extends Serializable\n\nobject Collection {\n\n  implicit class CollectionOps(collection: Collection) {\n\n    def toData =\n      CollectionData(\n        position = collection.position,\n        name = collection.name,\n        collectionType = collection.collectionType,\n        icon = collection.icon,\n        themedColorIndex = collection.themedColorIndex,\n        appsCategory = collection.appsCategory,\n        cards = collection.cards map (_.toData),\n        moment = collection.moment map (_.toData),\n        originalSharedCollectionId = collection.originalSharedCollectionId,\n        sharedCollectionId = collection.sharedCollectionId,\n        sharedCollectionSubscribed = collection.sharedCollectionSubscribed,\n        publicCollectionStatus = collection.publicCollectionStatus)\n\n  }\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/CollectionProcessConfig.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.NineCardsCategory\n\ncase class CollectionProcessConfig(namesCategories: Map[NineCardsCategory, String])\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Contact.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types._\n\ncase class Contact(\n    name: String,\n    lookupKey: String,\n    photoUri: String,\n    hasPhone: Boolean = false,\n    favorite: Boolean = false,\n    info: Option[ContactInfo] = None)\n\ncase class ContactInfo(emails: Seq[ContactEmail], phones: Seq[ContactPhone])\n\ncase class ContactEmail(address: String, category: EmailCategory)\n\ncase class ContactPhone(number: String, category: PhoneCategory)\n\ncase class LastCallsContact(\n    hasContact: Boolean,\n    number: String,\n    title: String,\n    photoUri: Option[String] = None,\n    lookupKey: Option[String] = None,\n    lastCallDate: Long,\n    calls: Seq[Call])\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Conversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport android.appwidget.{AppWidgetProviderInfo => AndroidAppWidgetProviderInfo}\nimport cards.nine.models.types.WidgetResizeMode\n\ntrait Conversions {\n\n  def toWidget(\n      androidAppWidgetProviderInfo: AndroidAppWidgetProviderInfo,\n      widgetLabel: String,\n      userHashCode: Option[Int]): AppWidget = {\n\n    import androidAppWidgetProviderInfo._\n\n    AppWidget(\n      userHashCode = userHashCode,\n      autoAdvanceViewId = autoAdvanceViewId,\n      initialLayout = initialLayout,\n      minHeight = minHeight,\n      minResizeHeight = minResizeHeight,\n      minResizeWidth = minResizeWidth,\n      minWidth = minWidth,\n      className = provider.getClassName,\n      packageName = provider.getPackageName,\n      resizeMode = WidgetResizeMode(resizeMode),\n      updatePeriodMillis = updatePeriodMillis,\n      label = widgetLabel,\n      preview = previewImage)\n  }\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/DockApp.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.DockType\n\ncase class DockApp(\n    id: Int,\n    name: String,\n    dockType: DockType,\n    intent: NineCardsIntent,\n    imagePath: String,\n    position: Int)\n\ncase class DockAppData(\n    name: String,\n    dockType: DockType,\n    intent: NineCardsIntent,\n    imagePath: String,\n    position: Int)\n\nobject DockApp {\n\n  implicit class DockAppOps(dockApp: DockApp) {\n\n    def toData =\n      DockAppData(\n        name = dockApp.name,\n        dockType = dockApp.dockType,\n        intent = dockApp.intent,\n        imagePath = dockApp.imagePath,\n        position = dockApp.position)\n\n  }\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/IconResize.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\ncase class IconResize(width: Int, height: Int)\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Intent.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nsealed trait IntentAction\n\ncase class AppAction(packageName: String, className: String) extends IntentAction\n\ncase class AppGooglePlayAction(googlePlayUrl: String, packageName: String) extends IntentAction\n\ncase class AppLauncherAction(packageName: String) extends IntentAction\n\ncase class AppSettingsAction(packageName: String) extends IntentAction\n\ncase class AppUninstallAction(packageName: String) extends IntentAction\n\ncase class ContactAction(lookupKey: String) extends IntentAction\n\ncase class EmailAction(email: String, titleDialog: String) extends IntentAction\n\ncase object GlobalSettingsAction extends IntentAction\n\ncase object GooglePlayStoreAction extends IntentAction\n\ncase object GoogleWeatherAction extends IntentAction\n\ncase class PhoneCallAction(phoneNumber: String) extends IntentAction\n\ncase class PhoneDialAction(maybePhoneNumber: Option[String]) extends IntentAction\n\ncase class PhoneSmsAction(phoneNumber: String) extends IntentAction\n\ncase object SearchGlobalAction extends IntentAction\n\ncase object SearchVoiceAction extends IntentAction\n\ncase object SearchWebAction extends IntentAction\n\ncase class ShareAction(text: String, titleDialog: String) extends IntentAction\n\ncase class UrlAction(url: String) extends IntentAction\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/IterableCursor.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport android.database.Cursor\n\nimport scala.annotation.tailrec\n\ntrait IterableCursor[T] {\n  def count(): Int\n\n  def moveToPosition(pos: Int): T\n\n  def close(): Unit\n}\n\nobject IterableCursor {\n\n  implicit class RichCursor(c: Cursor) {\n    private def implicitIterator[T](f: => T) = new IterableCursor[T] {\n      override def count(): Int = if (c.isClosed) 0 else c.getCount\n      override def moveToPosition(pos: Int): T = {\n        c.moveToPosition(pos)\n        f\n      }\n      override def close(): Unit = if (!c.isClosed) c.close()\n    }\n\n    private def implicitIter[T](f: => T) = implicitIterator[T](f)\n\n    def toIterator[T](conversionFunction: Cursor => T) = implicitIter {\n      conversionFunction(c)\n    }\n\n  }\n\n  @tailrec\n  def toSeq[T](iterator: IterableCursor[T], pos: Int = 0, seq: Seq[T] = Seq.empty): Seq[T] =\n    if (pos >= iterator.count()) {\n      seq\n    } else {\n      toSeq(iterator, pos + 1, seq :+ iterator.moveToPosition(pos))\n    }\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Iterables.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.commons.javaNull\nimport cards.nine.models.types.Misc\n\nclass IterableAppCursor[T](cursor: IterableCursor[T], f: T => Application)\n    extends IterableApplicationData {\n\n  override def count(): Int = cursor.count()\n\n  override def moveToPosition(pos: Int): ApplicationData = f(cursor.moveToPosition(pos)).toData\n\n  override def close(): Unit = cursor.close()\n\n}\n\nclass EmptyIterableApps() extends IterableAppCursor(javaNull, javaNull) {\n  val emptyApp                                           = ApplicationData(\"\", \"\", \"\", Misc, 0, 0, \"\", installedFromGooglePlay = false)\n  override def count(): Int                              = 0\n  override def moveToPosition(pos: Int): ApplicationData = emptyApp\n  override def close(): Unit                             = {}\n}\n\nclass IterableContacts(cursor: IterableCursor[Contact]) extends IterableCursor[Contact] {\n\n  override def count(): Int = cursor.count()\n\n  override def moveToPosition(pos: Int): Contact = cursor.moveToPosition(pos)\n\n  override def close(): Unit = cursor.close()\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/LauncherExecutorProcessConfig.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\ncase class LauncherExecutorProcessConfig(\n    googlePlayUrl: String,\n    titleEmailDialogChooser: String,\n    titleShareDialogChooser: String)\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Moment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types._\n\ncase class Moment(\n    id: Int,\n    collectionId: Option[Int],\n    timeslot: Seq[MomentTimeSlot],\n    wifi: Seq[String],\n    bluetooth: Seq[String],\n    headphone: Boolean,\n    momentType: NineCardsMoment,\n    widgets: Option[Seq[WidgetData]] = None)\n\ncase class MomentData(\n    collectionId: Option[Int],\n    timeslot: Seq[MomentTimeSlot],\n    wifi: Seq[String],\n    bluetooth: Seq[String],\n    headphone: Boolean,\n    momentType: NineCardsMoment,\n    widgets: Option[Seq[WidgetData]] = None)\n\ncase class MomentTimeSlot(from: String, to: String, days: Seq[Int])\n\nobject Moment {\n\n  implicit class MomentOps(moment: Moment) {\n\n    def toData =\n      MomentData(\n        collectionId = moment.collectionId,\n        timeslot = moment.timeslot,\n        wifi = moment.wifi,\n        bluetooth = moment.bluetooth,\n        headphone = moment.headphone,\n        momentType = moment.momentType,\n        widgets = moment.widgets)\n  }\n\n  implicit class MomentTimeSlotOps(moment: NineCardsMoment) {\n\n    def toMomentTimeSlot: Seq[MomentTimeSlot] =\n      moment match {\n        case HomeMorningMoment =>\n          Seq(MomentTimeSlot(from = \"08:00\", to = \"19:00\", days = Seq(1, 1, 1, 1, 1, 1, 1)))\n        case WorkMoment =>\n          Seq(MomentTimeSlot(from = \"08:00\", to = \"17:00\", days = Seq(0, 1, 1, 1, 1, 1, 0)))\n        case HomeNightMoment =>\n          Seq(\n            MomentTimeSlot(from = \"19:00\", to = \"23:59\", days = Seq(1, 1, 1, 1, 1, 1, 1)),\n            MomentTimeSlot(from = \"00:00\", to = \"08:00\", days = Seq(1, 1, 1, 1, 1, 1, 1)))\n        case StudyMoment =>\n          Seq(MomentTimeSlot(from = \"08:00\", to = \"17:00\", days = Seq(0, 1, 1, 1, 1, 1, 0)))\n        case MusicMoment       => Seq.empty\n        case CarMoment         => Seq.empty\n        case SportMoment       => Seq.empty\n        case OutAndAboutMoment => Seq.empty\n        case _                 => Seq.empty\n      }\n  }\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/NineCardsBluetoothDevice.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.BluetoothType\n\ncase class NineCardsBluetoothDevice(name: String, address: String, bluetoothType: BluetoothType)\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/NineCardsIntent.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport android.content.Intent\nimport android.content.Intent._\nimport android.net.Uri\nimport cards.nine.models.NineCardsIntentExtras._\nimport play.api.libs.json._\n\nimport scala.collection.JavaConversions._\nimport scala.util.Try\n\ncase class NineCardsIntent(intentExtras: NineCardsIntentExtras) extends Intent {\n\n  def fill(intent: Intent) =\n    fillIn(\n      intent,\n      FILL_IN_ACTION | FILL_IN_DATA | FILL_IN_CATEGORIES | FILL_IN_COMPONENT | FILL_IN_PACKAGE |\n        FILL_IN_SOURCE_BOUNDS | FILL_IN_SELECTOR | FILL_IN_CLIP_DATA)\n\n  def extractPackageName(): Option[String] =\n    Option(intentExtras.package_name.getOrElse(getStringExtra(nineCardExtraPackageName)))\n\n  def extractClassName(): Option[String] =\n    Option(intentExtras.class_name.getOrElse(getStringExtra(nineCardExtraClassName)))\n\n  def extractLookup(): Option[String] =\n    Option(intentExtras.contact_lookup_key.getOrElse(getStringExtra(nineCardExtraLookup)))\n\n  def extractPhone(): Option[String] =\n    Option(intentExtras.tel.getOrElse(getStringExtra(nineCardExtraPhone)))\n\n  def extractEmail(): Option[String] =\n    Option(intentExtras.email.getOrElse(getStringExtra(nineCardExtraEmail)))\n\n  def extractUrlAd(): Option[String] =\n    Option(intentExtras.url_ad.getOrElse(getStringExtra(nineCardExtraUrlAd)))\n\n  def toIntent: Intent = {\n    val intent = new Intent(this)\n    extractPackageName() map (packageName =>\n                                intent.putExtra(nineCardExtraPackageName, packageName))\n    extractClassName() map (className => intent.putExtra(nineCardExtraClassName, className))\n    extractLookup() map (lookupKey => intent.putExtra(nineCardExtraLookup, lookupKey))\n    extractPhone() map (phone => intent.putExtra(nineCardExtraPhone, phone))\n    extractEmail() map (email => intent.putExtra(nineCardExtraEmail, email))\n    extractUrlAd() map (urlAd => intent.putExtra(nineCardExtraUrlAd, urlAd))\n    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n    intent\n  }\n}\n\ncase class NineCardsIntentExtras(\n    contact_lookup_key: Option[String] = None,\n    tel: Option[String] = None,\n    email: Option[String] = None,\n    url_ad: Option[String] = None,\n    package_name: Option[String] = None,\n    class_name: Option[String] = None)\n\nobject NineCardsIntentExtras {\n  val nineCardExtraLookup: String      = \"contact_lookup_key\"\n  val nineCardExtraPhone: String       = \"tel\"\n  val nineCardExtraEmail: String       = \"email\"\n  val nineCardExtraUrlAd: String       = \"url_ad\"\n  val nineCardExtraPackageName: String = \"package_name\"\n  val nineCardExtraClassName: String   = \"class_name\"\n  val openApp: String                  = \"cards.nine.OPEN_APP\"\n  val openNoInstalledApp: String       = \"cards.nine.OPEN_RECOMMENDED_APP\"\n  val openSms: String                  = \"cards.nine.OPEN_SMS\"\n  val openPhone: String                = \"cards.nine.OPEN_PHONE\"\n  val openEmail: String                = \"cards.nine.OPEN_EMAIL\"\n  val openContact: String              = \"cards.nine.OPEN_CONTACT\"\n}\n\nobject NineCardsIntentImplicits {\n\n  implicit val extrasReads = Json.reads[NineCardsIntentExtras]\n\n  implicit val extrasWrites = Json.writes[NineCardsIntentExtras]\n\n  implicit val nineCardIntentReads = new Reads[NineCardsIntent] {\n    def reads(js: JsValue): JsResult[NineCardsIntent] = {\n      val intent = NineCardsIntent((js \\ \"intentExtras\").as[NineCardsIntentExtras])\n      (for {\n        packageName <- (js \\ \"packageName\").asOpt[String]\n        className   <- (js \\ \"className\").asOpt[String]\n      } yield {\n        intent.setClassName(packageName, className)\n      }) getOrElse {\n        (js \\ \"packageName\").asOpt[String] foreach intent.setPackage\n      }\n      // We have to set data and type values together because Android SDK\n      // clear type value when data values is set and vice versa\n      (for {\n        dataString <- (js \\ \"dataString\").asOpt[String]\n        typeString <- (js \\ \"type\").asOpt[String]\n      } yield {\n        intent.setDataAndTypeAndNormalize(Uri.parse(dataString), typeString)\n      }) getOrElse {\n        (js \\ \"dataString\")\n          .asOpt[String] map (dataString =>\n                                intent.setDataAndNormalize(Uri.parse(dataString))) getOrElse {\n          (js \\ \"type\").asOpt[String] foreach intent.setTypeAndNormalize\n        }\n      }\n      (js \\ \"categories\").asOpt[List[String]] foreach (_ foreach intent.addCategory)\n      (js \\ \"action\").asOpt[String] map fixActionPackage foreach intent.setAction\n      (js \\ \"extras\").asOpt[JsObject] foreach (_.value map {\n        case (name, value) => matchExtras(intent, name, value)\n      })\n      (js \\ \"flags\").asOpt[Int] foreach intent.setFlags\n      JsSuccess(intent)\n    }\n\n    val legacyPackage = \"com.fortysevendeg.ninecardslauncher.\"\n\n    def fixActionPackage(action: String): String =\n      if (action.startsWith(legacyPackage)) action.replace(legacyPackage, \"cards.nine.\")\n      else action\n\n    def matchExtras(intent: NineCardsIntent, name: String, value: JsValue) = value match {\n      case s: JsString  => intent.putExtra(name, s.as[String])\n      case s: JsNumber  => intent.putExtra(name, s.as[Int])\n      case s: JsBoolean => intent.putExtra(name, s.as[Boolean])\n      case _            =>\n    }\n\n  }\n\n  implicit val nineCardsIntentWrites = new Writes[NineCardsIntent] {\n    def writes(intent: NineCardsIntent): JsValue = {\n      val jsExtras = Option(intent.getExtras) map { extras =>\n        (extras.keySet() flatMap { key =>\n          Try {\n            extras.get(key).asInstanceOf[Any] match {\n              case s: String  => key -> Json.toJsFieldJsValueWrapper(s)\n              case i: Int     => key -> Json.toJsFieldJsValueWrapper(i)\n              case b: Boolean => key -> Json.toJsFieldJsValueWrapper(b)\n              case f: Float   => key -> Json.toJsFieldJsValueWrapper(f)\n              case d: Double  => key -> Json.toJsFieldJsValueWrapper(d)\n            }\n          }.toOption\n        }).toSeq\n      }\n      val extras        = jsExtras map (e => Json.obj(e: _*)) getOrElse Json.obj()\n      val categoriesSet = Option(intent.getCategories) map (_.toSet map JsString) getOrElse Set.empty\n      val categories    = JsArray(categoriesSet.toSeq)\n      Json.obj(\n        \"intentExtras\" -> Json.toJsFieldJsValueWrapper(intent.intentExtras),\n        \"className\" -> Json.toJsFieldJsValueWrapper(\n          (Option(intent.getComponent) map (_.getClassName)).orNull),\n        \"packageName\" -> Json.toJsFieldJsValueWrapper(\n          (Option(intent.getComponent) map (_.getPackageName)).orNull),\n        \"categories\" -> Json.toJsFieldJsValueWrapper(categories),\n        \"action\"     -> Json.toJsFieldJsValueWrapper(intent.getAction),\n        \"extras\"     -> Json.toJsFieldJsValueWrapper(extras),\n        \"flags\"      -> Json.toJsFieldJsValueWrapper(intent.getFlags),\n        \"type\"       -> Json.toJsFieldJsValueWrapper(intent.getType),\n        \"dataString\" -> Json.toJsFieldJsValueWrapper(\n          (Option(intent.getData) map (_.toString)).orNull))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/NineCardsIntentConversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.NineCardsIntentExtras._\nimport cards.nine.models.NineCardsIntentImplicits._\nimport play.api.libs.json.Json\n\ntrait NineCardsIntentConversions {\n\n  def jsonToNineCardIntent(json: String) = Json.parse(json).as[NineCardsIntent]\n\n  def nineCardIntentToJson(intent: NineCardsIntent) = Json.toJson(intent).toString()\n\n  def toNineCardIntent(item: ApplicationData) = {\n    val intent = NineCardsIntent(\n      NineCardsIntentExtras(\n        package_name = Option(item.packageName),\n        class_name = Option(item.className)))\n    intent.setAction(openApp)\n    intent.setClassName(item.packageName, item.className)\n    intent\n  }\n\n  def toNineCardIntent(app: Application) = {\n    val intent = NineCardsIntent(\n      NineCardsIntentExtras(\n        package_name = Option(app.packageName),\n        class_name = Option(app.className)))\n    intent.setAction(openApp)\n    intent.setClassName(app.packageName, app.className)\n    intent\n  }\n\n  def packageToNineCardIntent(packageName: String): NineCardsIntent = {\n    val intent = NineCardsIntent(NineCardsIntentExtras(package_name = Option(packageName)))\n    intent.setAction(openNoInstalledApp)\n    intent\n  }\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Rank.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.{NineCardsCategory, NineCardsMoment}\n\ncase class PackagesByCategory(category: NineCardsCategory, packages: Seq[String])\n\ncase class PackagesByMoment(moment: NineCardsMoment, packages: Seq[String])\n\ncase class WidgetsByMoment(moment: NineCardsMoment, widgets: Seq[AppWidget])\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/SharedCollection.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.{NineCardsCategory, PublicCollectionStatus}\n\ncase class SharedCollection(\n    id: String,\n    sharedCollectionId: String,\n    publishedOn: Long,\n    author: String,\n    name: String,\n    packages: Seq[String],\n    resolvedPackages: Seq[SharedCollectionPackage],\n    views: Int,\n    subscriptions: Option[Int],\n    category: NineCardsCategory,\n    icon: String,\n    community: Boolean,\n    locallyAdded: Option[Boolean],\n    publicCollectionStatus: PublicCollectionStatus)\n\ncase class SharedCollectionPackage(\n    packageName: String,\n    title: String,\n    icon: String,\n    category: Option[NineCardsCategory],\n    stars: Double,\n    downloads: String,\n    free: Boolean)\n\ncase class Subscription(\n    id: Int,\n    sharedCollectionId: String,\n    name: String,\n    apps: Int,\n    icon: String,\n    themedColorIndex: Int,\n    subscribed: Boolean)\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/ShortCut.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport android.content.Intent\nimport android.graphics.drawable.Drawable\n\ncase class Shortcut(title: String, icon: Option[Drawable], intent: Intent)\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/TermCounter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\ncase class TermCounter(term: String, count: Int)\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Theme.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport android.graphics.Color\nimport cards.nine.commons.ops.ColorOps._\nimport cards.nine.models.types.theme._\nimport play.api.libs.json._\n\nimport scala.util.Random\n\ncase class NineCardsTheme(\n    name: String,\n    parent: ThemeType,\n    styles: Seq[ThemeStyle],\n    themeColors: ThemeColors) {\n\n  val lineRatio = 0.3f\n\n  def get(style: ThemeStyleType): Int = styles.find(_.styleType == style) map (_.color) getOrElse {\n    android.util.Log.i(\"9Cards\", s\"The selected theme doesn't have the $style property\")\n    Color.TRANSPARENT\n  }\n\n  def getLineColor: Int = parent match {\n    case ThemeLight => get(DrawerBackgroundColor).dark(lineRatio)\n    case ThemeDark  => get(DrawerBackgroundColor).light(lineRatio)\n  }\n\n  def getIndexColor(index: Int): Int =\n    themeColors.colors.lift(index).getOrElse(themeColors.defaultColor)\n\n  def getRandomIndexColor: Int = getIndexColor(Random.nextInt(themeColors.colors.size))\n}\n\ncase class ThemeColors(defaultColor: Int, colors: Seq[Int])\n\ncase class ThemeStyle(styleType: ThemeStyleType, color: Int)\n\nobject NineCardsThemeImplicits {\n\n  implicit val themeTypeReads = new Reads[ThemeType] {\n    override def reads(js: JsValue) = js.as[String] match {\n      case \"light\" => JsSuccess(ThemeLight)\n      case \"dark\"  => JsSuccess(ThemeDark)\n      case _       => JsError(s\"Theme type should be 'light' or 'dark'\")\n    }\n  }\n\n  implicit val themeTypeWrites = new Writes[ThemeType] {\n    override def writes(themeType: ThemeType) = themeType match {\n      case ThemeLight => JsString(\"light\")\n      case ThemeDark  => JsString(\"dark\")\n    }\n  }\n\n  implicit val themeStyleTypeReads = new Reads[ThemeStyleType] {\n\n    def reads(js: JsValue) = js.as[String] match {\n      case \"PrimaryColor\"                        => JsSuccess(PrimaryColor)\n      case \"DockPressedColor\"                    => JsSuccess(DockPressedColor)\n      case \"CardLayoutBackgroundColor\"           => JsSuccess(CardLayoutBackgroundColor)\n      case \"CardBackgroundColor\"                 => JsSuccess(CardBackgroundColor)\n      case \"CardBackgroundPressedColor\"          => JsSuccess(CardBackgroundPressedColor)\n      case \"CardTextColor\"                       => JsSuccess(CardTextColor)\n      case \"CollectionDetailTextTabDefaultColor\" => JsSuccess(CollectionDetailTextTabDefaultColor)\n      case \"CollectionDetailTextTabSelectedColor\" =>\n        JsSuccess(CollectionDetailTextTabSelectedColor)\n      case \"DrawerTabsBackgroundColor\" => JsSuccess(DrawerTabsBackgroundColor)\n      case \"DrawerBackgroundColor\"     => JsSuccess(DrawerBackgroundColor)\n      case \"DrawerTextColor\"           => JsSuccess(DrawerTextColor)\n      case \"SearchBackgroundColor\"     => JsSuccess(SearchBackgroundColor)\n      case \"SearchGoogleColor\"         => JsSuccess(SearchGoogleColor)\n      case \"SearchIconsColor\"          => JsSuccess(SearchIconsColor)\n      case \"SearchTextColor\"           => JsSuccess(SearchTextColor)\n      case \"SearchPressedColor\"        => JsSuccess(SearchPressedColor)\n      case \"DrawerIconColor\"           => JsSuccess(DrawerIconColor)\n      case _                           => JsError(\"Theme style type not allowed\")\n    }\n  }\n\n  implicit val themeStyleTypeWrites = new Writes[ThemeStyleType] {\n\n    def writes(styleType: ThemeStyleType) = styleType match {\n      case PrimaryColor               => Json.toJson(\"PrimaryColor\")\n      case DockPressedColor           => Json.toJson(\"DockPressedColor\")\n      case CardLayoutBackgroundColor  => Json.toJson(\"CardLayoutBackgroundColor\")\n      case CardBackgroundColor        => Json.toJson(\"CardBackgroundColor\")\n      case CardBackgroundPressedColor => Json.toJson(\"CardBackgroundPressedColor\")\n      case CardTextColor              => Json.toJson(\"CardTextColor\")\n      case CollectionDetailTextTabDefaultColor =>\n        Json.toJson(\"CollectionDetailTextTabDefaultColor\")\n      case CollectionDetailTextTabSelectedColor =>\n        Json.toJson(\"CollectionDetailTextTabSelectedColor\")\n      case DrawerTabsBackgroundColor => Json.toJson(\"DrawerTabsBackgroundColor\")\n      case DrawerBackgroundColor     => Json.toJson(\"DrawerBackgroundColor\")\n      case DrawerTextColor           => Json.toJson(\"DrawerTextColor\")\n      case SearchBackgroundColor     => Json.toJson(\"SearchBackgroundColor\")\n      case SearchGoogleColor         => Json.toJson(\"SearchGoogleColor\")\n      case SearchIconsColor          => Json.toJson(\"SearchIconsColor\")\n      case SearchTextColor           => Json.toJson(\"SearchTextColor\")\n      case SearchPressedColor        => Json.toJson(\"SearchPressedColor\")\n      case DrawerIconColor           => Json.toJson(\"DrawerIconColor\")\n    }\n  }\n\n  implicit val themeStyleReads = new Reads[ThemeStyle] {\n\n    def reads(js: JsValue) = {\n\n      val themeStyleType = (js \\ \"styleType\").as[ThemeStyleType]\n\n      val themeStyleColor = Color.parseColor((js \\ \"color\").as[String])\n\n      JsSuccess(ThemeStyle(styleType = themeStyleType, color = themeStyleColor))\n    }\n  }\n\n  implicit val themeStyleWrites = new Writes[ThemeStyle] {\n\n    def writes(themeStyle: ThemeStyle) =\n      Json.obj(\n        \"styleType\" -> Json.toJsFieldJsValueWrapper(themeStyle.styleType),\n        \"color\"     -> Json.toJsFieldJsValueWrapper(themeStyle.color.colorToString)\n      )\n  }\n\n  implicit val themeColorsReads = new Reads[ThemeColors] {\n\n    def reads(js: JsValue) = {\n\n      val defaultColor = Color.parseColor((js \\ \"defaultColor\").as[String])\n\n      val colors = (js \\ \"colors\").as[Array[String]].map(Color.parseColor)\n\n      JsSuccess(ThemeColors(defaultColor, colors))\n    }\n\n  }\n\n  implicit val themeColorsWrites = new Writes[ThemeColors] {\n\n    def writes(themeColors: ThemeColors) =\n      Json.obj(\n        \"defaultColor\" -> Json.toJsFieldJsValueWrapper(themeColors.defaultColor.colorToString),\n        \"color\"        -> Json.toJsFieldJsValueWrapper(themeColors.colors.map(_.colorToString).toArray)\n      )\n\n  }\n\n  implicit val nineCardsThemeReads = Json.reads[NineCardsTheme]\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/TrackEvent.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.{Action, Category, Screen, Value}\n\ncase class TrackEvent(\n    screen: Screen,\n    category: Category,\n    action: Action,\n    label: Option[String] = None,\n    value: Option[Value] = None)\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/User.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\ncase class User(\n    id: Int,\n    email: Option[String],\n    apiKey: Option[String],\n    sessionToken: Option[String],\n    deviceToken: Option[String],\n    marketToken: Option[String],\n    deviceName: Option[String],\n    deviceCloudId: Option[String],\n    userProfile: UserProfile)\n\ncase class UserData(\n    email: Option[String],\n    apiKey: Option[String],\n    sessionToken: Option[String],\n    deviceToken: Option[String],\n    marketToken: Option[String],\n    deviceName: Option[String],\n    deviceCloudId: Option[String],\n    userProfile: UserProfile)\n\ncase class UserProfile(name: Option[String], avatar: Option[String], cover: Option[String])\n\nobject User {\n\n  implicit class UserOps(user: User) {\n\n    def toData =\n      UserData(\n        email = user.email,\n        apiKey = user.apiKey,\n        sessionToken = user.sessionToken,\n        deviceToken = user.deviceToken,\n        marketToken = user.marketToken,\n        deviceName = user.deviceName,\n        deviceCloudId = user.deviceCloudId,\n        userProfile = user.userProfile)\n\n  }\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/Widget.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models\n\nimport cards.nine.models.types.{WidgetResizeMode, WidgetType}\n\ncase class AppsWithWidgets(packageName: String, name: String, widgets: Seq[AppWidget])\n\ncase class AppWidget(\n    userHashCode: Option[Int],\n    autoAdvanceViewId: Int,\n    initialLayout: Int,\n    minHeight: Int,\n    minResizeHeight: Int,\n    minResizeWidth: Int,\n    minWidth: Int,\n    className: String,\n    packageName: String,\n    resizeMode: WidgetResizeMode,\n    updatePeriodMillis: Int,\n    label: String,\n    preview: Int)\n\ncase class Widget(\n    id: Int,\n    momentId: Int,\n    packageName: String,\n    className: String,\n    appWidgetId: Option[Int],\n    area: WidgetArea,\n    widgetType: WidgetType,\n    label: Option[String],\n    imagePath: Option[String],\n    intent: Option[NineCardsIntent])\n\ncase class WidgetData(\n    momentId: Int = 0,\n    packageName: String,\n    className: String,\n    appWidgetId: Option[Int],\n    area: WidgetArea,\n    widgetType: WidgetType,\n    label: Option[String],\n    imagePath: Option[String],\n    intent: Option[NineCardsIntent])\n\ncase class WidgetArea(startX: Int, startY: Int, spanX: Int, spanY: Int) {\n\n  def intersect(other: WidgetArea, limits: Option[(Int, Int)] = None): Boolean = {\n    def valueInRange(value: Int, min: Int, max: Int) = (value >= min) && (value < max)\n\n    val xOverlap = valueInRange(startX, other.startX, other.startX + other.spanX) ||\n        valueInRange(other.startX, startX, startX + spanX)\n\n    val yOverlap = valueInRange(startY, other.startY, other.startY + other.spanY) ||\n        valueInRange(other.startY, startY, startY + spanY)\n\n    val outOfLimits = limits exists {\n      case (x, y) => (startX < 0) || (startY < 0) || (startX + spanX > x) || (startY + spanY > y)\n    }\n\n    (xOverlap && yOverlap) || outOfLimits\n  }\n\n  def isValid(columns: Int, rows: Int): Boolean =\n    spanX > 0 &&\n      spanY > 0 &&\n      startX >= 0 && startX + spanX <= columns &&\n      startY >= 0 && startY + spanY <= rows\n\n}\n\nobject Widget {\n\n  implicit class WidgetOps(widget: Widget) {\n\n    def toData =\n      WidgetData(\n        momentId = widget.momentId,\n        packageName = widget.packageName,\n        className = widget.className,\n        appWidgetId = widget.appWidgetId,\n        area = widget.area,\n        widgetType = widget.widgetType,\n        label = widget.label,\n        imagePath = widget.imagePath,\n        intent = widget.intent)\n\n  }\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/package.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine\n\npackage object models {\n\n  type IterableApplicationData = IterableCursor[ApplicationData]\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/reads/MomentImplicits.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.reads\n\nimport cards.nine.models.MomentTimeSlot\n\nobject MomentImplicits {\n\n  import play.api.libs.json._\n\n  implicit val momentTimeSlotReads = Json.reads[MomentTimeSlot]\n\n  implicit val momentTimeSlotWrites = Json.writes[MomentTimeSlot]\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/Action.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait Action {\n  def name: String\n}\n\n/* AppDrawerScreen */\n\ncase object UsingFastScrollerAction extends Action {\n  override def name: String = \"UsingFastScroller\"\n}\n\ncase object GoToContactsAction extends Action {\n  override def name: String = \"GoToContacts\"\n}\n\ncase object GoToAppsAction extends Action {\n  override def name: String = \"GoToApps\"\n}\n\ncase object AddAppToCollectionAction extends Action {\n  override def name: String = \"AddAppToCollection\"\n}\n\ncase object AddContactToCollectionAction extends Action {\n  override def name: String = \"AddContactToCollection\"\n}\n\ncase object GoToGooglePlayButtonAction extends Action {\n  override def name: String = \"GoToGooglePlayButton\"\n}\n\ncase object GoToGoogleCallButtonAction extends Action {\n  override def name: String = \"GoToGoogleCallButton\"\n}\n\ncase object GoToFiltersByButtonAction extends Action {\n  override def name: String = \"GoToFiltersByButton\"\n}\n\n/* CollectionDetailScreen */\n\ncase object NavigationBarAction extends Action {\n  override def name: String = \"NavigationBar\"\n}\n\ncase object ReorderApplicationAction extends Action {\n  override def name: String = \"ReorderApplication\"\n}\n\ncase object MoveApplicationsAction extends Action {\n  override def name: String = \"MoveApplications\"\n}\n\ncase object RemoveApplicationsAction extends Action {\n  override def name: String = \"RemoveApplicationsAction\"\n}\n\ncase object CloseCollectionByGestureAction extends Action {\n  override def name: String = \"CloseCollectionByGesture\"\n}\n\ncase object AddShortcutByFabAction extends Action {\n  override def name: String = \"AddShortcutByFab\"\n}\n\ncase object AddShortcutFromReceiverAction extends Action {\n  override def name: String = \"AddShortcutFromReceiver\"\n}\n\ncase object AddRecommendationByFabAction extends Action {\n  override def name: String = \"AddRecommendationByFab\"\n}\n\ncase object AddContactByFabAction extends Action {\n  override def name: String = \"AddContactByFab\"\n}\n\ncase object AddAppsByFabAction extends Action {\n  override def name: String = \"AddAppsByFab\"\n}\n\ncase object RemoveAppsByFabAction extends Action {\n  override def name: String = \"RemoveAppsByFabAction\"\n}\n\ncase object AddCardByMenuAction extends Action {\n  override def name: String = \"AddCardByMenu\"\n}\n\ncase object PublishCollectionByMenuAction extends Action {\n  override def name: String = \"PublishCollectionByMenu\"\n}\n\ncase object ShareCollectionAfterPublishingAction extends Action {\n  override def name: String = \"ShareCollectionAfterPublishing\"\n}\n\ncase object ShareCollectionByMenuAction extends Action {\n  override def name: String = \"ShareCollectionByMenu\"\n}\n\ncase object OpenCardAction extends Action {\n  override def name: String = \"OpenCard\"\n}\n\ncase object AddedToCollectionAction extends Action {\n  override def name: String = \"AddedToCollection\"\n}\n\ncase object RemovedFromCollectionAction extends Action {\n  override def name: String = \"RemovedFromCollection\"\n}\n\n/* HomeScreen */\n\ncase object OpenCollectionTitleAction extends Action {\n  override def name: String = \"OpenCollectionTitle\"\n}\n\ncase object OpenCollectionOrderAction extends Action {\n  override def name: String = \"OpenCollectionOrder\"\n}\n\ncase object DeleteCollectionAction extends Action {\n  override def name: String = \"DeleteCollection\"\n}\n\ncase object ReorderCollectionAction extends Action {\n  override def name: String = \"ReorderCollection\"\n}\n\ncase object UsingSearchByKeyboardAction extends Action {\n  override def name: String = \"UsingSearchByKeyboard\"\n}\n\ncase object UsingSearchByVoiceAction extends Action {\n  override def name: String = \"UsingSearchByVoice\"\n}\n\ncase object CreateNewCollectionAction extends Action {\n  override def name: String = \"CreateNewCollection\"\n}\n\ncase object EditCollectionAction extends Action {\n  override def name: String = \"EditCollection\"\n}\n\ncase object OpenMyCollectionsAction extends Action {\n  override def name: String = \"OpenMyCollections\"\n}\n\ncase object OpenPublicCollectionsAction extends Action {\n  override def name: String = \"OpenPublicCollections\"\n}\n\ncase object CreateNewCollectionFromMyCollectionAction extends Action {\n  override def name: String = \"CreateNewCollectionFromMyCollection\"\n}\n\ncase object CreateNewCollectionFromPublicCollectionAction extends Action {\n  override def name: String = \"CreateNewCollectionFromPublicCollection\"\n}\n\ncase object OpenDockAppTitleAction extends Action {\n  override def name: String = \"OpenDockAppTitle\"\n}\n\ncase object OpenDockAppOrderAction extends Action {\n  override def name: String = \"OpenDockAppOrder\"\n}\n\ncase object GoToAppDrawerAction extends Action {\n  override def name: String = \"GoToAppDrawer\"\n}\n\ncase object AppLinkReceivedAction extends Action {\n  override def name: String = \"AppLinkReceived\"\n}\n\ncase object SharedContentReceivedAction extends Action {\n  override def name: String = \"SharedContentReceived\"\n}\n\n/* LauncherScreen */\n\ncase object OpenAction extends Action {\n  override def name: String = \"Open\"\n}\n\n/* MomentsScreen */\n\ncase object OpenApplicationByMomentAction extends Action {\n  override def name: String = \"OpenApplicationByMoment\"\n}\n\ncase object EditMomentAction extends Action {\n  override def name: String = \"EditMoment\"\n}\n\ncase object ChangeMomentAction extends Action {\n  override def name: String = \"ChangeMoment\"\n}\n\ncase object AddMomentAction extends Action {\n  override def name: String = \"AddMoment\"\n}\n\ncase object AddWidgetAction extends Action {\n  override def name: String = \"AddWidget\"\n}\n\ncase object UnpinMomentAction extends Action {\n  override def name: String = \"UnpinMoment\"\n}\n\ncase object GoToWeatherAction extends Action {\n  override def name: String = \"GoToWeather\"\n}\n\ncase object QuickAccessToCollectionAction extends Action {\n  override def name: String = \"QuickAccessToCollection\"\n}\n\ncase object SetHoursAction extends Action {\n  override def name: String = \"SetHours\"\n}\n\ncase object SetWifiAction extends Action {\n  override def name: String = \"SetWifi\"\n}\n\ncase object SetBluetoothAction extends Action {\n  override def name: String = \"SetBluetooth\"\n}\n\ncase object DeleteMomentAction extends Action {\n  override def name: String = \"DeleteMoment\"\n}\n\n/* ProfileScreen */\n\ncase object AddToMyCollectionsFromProfileAction extends Action {\n  override def name: String = \"AddToMyCollectionsFromProfile\"\n}\n\ncase object ChangeConfigurationNameAction extends Action {\n  override def name: String = \"ChangeConfigurationName\"\n}\n\ncase object CopyConfigurationAction extends Action {\n  override def name: String = \"CopyConfiguration\"\n}\n\ncase object DeleteConfigurationAction extends Action {\n  override def name: String = \"DeleteConfiguration\"\n}\n\ncase object LogoutAction extends Action {\n  override def name: String = \"Logout\"\n}\n\ncase object ShareCollectionFromProfileAction extends Action {\n  override def name: String = \"ShareCollectionFromProfile\"\n}\n\ncase object ShowAccountsContentAction extends Action {\n  override def name: String = \"ShowAccountsContent\"\n}\n\ncase object ShowPublicationsContentAction extends Action {\n  override def name: String = \"ShowPublicationsContent\"\n}\n\ncase object ShowSubscriptionsContentAction extends Action {\n  override def name: String = \"ShowSubscriptionsContent\"\n}\n\ncase object SubscribeToCollectionAction extends Action {\n  override def name: String = \"SubscribeToCollection\"\n}\n\ncase object SynchronizeConfigurationAction extends Action {\n  override def name: String = \"SynchronizeConfiguration\"\n}\n\ncase object UnsubscribeFromCollectionAction extends Action {\n  override def name: String = \"UnsubscribeFromCollection\"\n}\n\n/* SliderMenuScreen */\n\ncase object GoToCollectionsByMenuAction extends Action {\n  override def name: String = \"GoToCollectionsByMenu\"\n}\n\ncase object GoToMomentsByMenuAction extends Action {\n  override def name: String = \"GoToMomentsByMenu\"\n}\n\ncase object GoToProfileByMenuAction extends Action {\n  override def name: String = \"GoToProfileByMenu\"\n}\n\ncase object GoToSendUsFeedbackAction extends Action {\n  override def name: String = \"GoToSendUsFeedback\"\n}\n\ncase object GoToHelpAction extends Action {\n  override def name: String = \"GoToHelp\"\n}\n\n/* WidgetScreen */\n\ncase object AddedWidgetToMomentAction extends Action {\n  override def name: String = \"AddedWidgetToMoment\"\n}\n\n/* WizardScreen */\n\ncase object ChooseAccountAction extends Action {\n  override def name: String = \"ChooseAccount\"\n}\n\ncase object ChooseNewConfigurationAction extends Action {\n  override def name: String = \"ChooseNewConfiguration\"\n}\n\ncase object ChooseExistingDeviceAction extends Action {\n  override def name: String = \"ChooseExistingDevice\"\n}\n\ncase object ChooseMomentAction extends Action {\n  override def name: String = \"ChooseMoment\"\n}\n\ncase object ChooseMomentWifiAction extends Action {\n  override def name: String = \"ChooseMomentWifi\"\n}\n\ncase object ChooseOtherMomentAction extends Action {\n  override def name: String = \"ChooseOtherMoment\"\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/AppPermission.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait AppPermission {\n  val value: String\n}\n\ncase object GetAccounts extends AppPermission {\n  override val value: String = android.Manifest.permission.GET_ACCOUNTS\n}\n\ncase object ReadContacts extends AppPermission {\n  override val value: String = android.Manifest.permission.READ_CONTACTS\n}\n\ncase object ReadCallLog extends AppPermission {\n  override val value: String = android.Manifest.permission.READ_CALL_LOG\n}\n\ncase object CallPhone extends AppPermission {\n  override val value: String = android.Manifest.permission.CALL_PHONE\n}\n\ncase object FineLocation extends AppPermission {\n  override val value: String = android.Manifest.permission.ACCESS_FINE_LOCATION\n}\n\nobject AppPermission {\n\n  def values = Seq(GetAccounts, ReadContacts, ReadCallLog, CallPhone, FineLocation)\n\n}\n\ncase class PermissionResult(permission: AppPermission, result: Boolean) {\n  def hasPermission(p: AppPermission): Boolean = permission == p && result\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/AwarenessFenceUpdate.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait AwarenessFenceUpdate {\n  val key: String\n}\n\ncase object HeadphonesFence extends AwarenessFenceUpdate {\n\n  override val key: String = \"HEADPHONE_PLUGGED_FENCE\"\n\n  val keyIn: String = s\"${key}_IN\"\n\n  val keyOut: String = s\"${key}_OUT\"\n}\n\ncase object InVehicleFence extends AwarenessFenceUpdate {\n  override val key: String = \"IN_VEHICLE_FENCE\"\n}\n\nobject AwarenessFenceUpdate {\n\n  val fences = Seq(HeadphonesFence, InVehicleFence)\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/BluetoothType.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nimport android.bluetooth.BluetoothClass\n\nsealed trait BluetoothType\n\ncase object AudioAndVideoBluetooth extends BluetoothType\n\ncase object ComputerBluetooth extends BluetoothType\n\ncase object HealthBluetooth extends BluetoothType\n\ncase object NetworkingBluetooth extends BluetoothType\n\ncase object PeripheralBluetooth extends BluetoothType\n\ncase object PhoneBluetooth extends BluetoothType\n\ncase object ToyBluetooth extends BluetoothType\n\ncase object WearableBluetooth extends BluetoothType\n\ncase object UnknownBluetooth extends BluetoothType\n\nobject BluetoothType {\n\n  def apply(t: Int): BluetoothType = t match {\n    case BluetoothClass.Device.Major.AUDIO_VIDEO => AudioAndVideoBluetooth\n    case BluetoothClass.Device.Major.COMPUTER    => ComputerBluetooth\n    case BluetoothClass.Device.Major.HEALTH      => HealthBluetooth\n    case BluetoothClass.Device.Major.NETWORKING  => NetworkingBluetooth\n    case BluetoothClass.Device.Major.PERIPHERAL  => PeripheralBluetooth\n    case BluetoothClass.Device.Major.PHONE       => PhoneBluetooth\n    case BluetoothClass.Device.Major.TOY         => ToyBluetooth\n    case BluetoothClass.Device.Major.WEARABLE    => WearableBluetooth\n    case _                                       => UnknownBluetooth\n  }\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/CallType.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nimport android.provider.CallLog\n\nsealed trait CallType\n\ncase object IncomingType extends CallType\n\ncase object OutgoingType extends CallType\n\ncase object MissedType extends CallType\n\ncase object OtherType extends CallType\n\nobject CallType {\n\n  def apply(mode: Int): CallType = mode match {\n    case CallLog.Calls.INCOMING_TYPE => IncomingType\n    case CallLog.Calls.OUTGOING_TYPE => OutgoingType\n    case CallLog.Calls.MISSED_TYPE   => MissedType\n    case _                           => OtherType\n  }\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/CardType.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait CardType {\n  val name: String\n  def isContact: Boolean =\n    this == PhoneCardType ||\n      this == EmailCardType ||\n      this == ContactCardType ||\n      this == SmsCardType\n}\n\ncase object AppCardType extends CardType {\n  override val name: String = \"APP\"\n}\n\ncase object NoInstalledAppCardType extends CardType {\n  override val name: String = \"NO_INSTALLED_APP\"\n}\n\ncase object PhoneCardType extends CardType {\n  override val name: String = \"PHONE\"\n}\n\ncase object ContactCardType extends CardType {\n  override val name: String = \"CONTACT\"\n}\n\ncase object EmailCardType extends CardType {\n  override val name: String = \"EMAIL\"\n}\n\ncase object SmsCardType extends CardType {\n  override val name: String = \"SMS\"\n}\n\ncase object ShortcutCardType extends CardType {\n  override val name: String = \"SHORTCUT\"\n}\n\ncase object RecommendedAppCardType extends CardType {\n  override val name: String = \"RECOMMENDED_APP\"\n}\n\ncase object NotFoundCardType extends CardType {\n  override val name: String = \"RECOMMENDED_APP\"\n}\n\nobject CardType {\n\n  val cardTypes = Seq(\n    AppCardType,\n    NoInstalledAppCardType,\n    PhoneCardType,\n    ContactCardType,\n    EmailCardType,\n    SmsCardType,\n    ShortcutCardType,\n    RecommendedAppCardType)\n\n  def apply(name: String): CardType = cardTypes find (_.name == name) getOrElse NotFoundCardType\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/Category.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait Category {\n  def name: String\n}\n\ncase object AccountCategory extends Category {\n  override def name: String = \"ACCOUNT\"\n}\n\ncase class AppCategory(nineCardCategory: NineCardsCategory) extends Category {\n  override def name: String = nineCardCategory.name\n}\n\ncase object EditMomentCategory extends Category {\n  override def name: String = \"EDIT MOMENT\"\n}\n\ncase object FastScrollerCategory extends Category {\n  override def name: String = \"FAST SCROLLER\"\n}\n\ncase object FreeCategory extends Category {\n  override def name: String = \"FREE\"\n}\n\ncase object GestureActionsCategory extends Category {\n  override def name: String = \"GESTURE ACTIONS\"\n}\n\ncase object LinksReceivedCategory extends Category {\n  override def name: String = \"LINKS RECEIVED\"\n}\n\ncase object IconBarCategory extends Category {\n  override def name: String = \"ICON BAR\"\n}\n\ncase class MomentCategory(moment: NineCardsMoment) extends Category {\n  override def name: String = moment.name\n}\n\ncase object MomentsMenuCategory extends Category {\n  override def name: String = \"MOMENTS MENU\"\n}\n\ncase object PublicationCategory extends Category {\n  override def name: String = \"PUBLICATION\"\n}\n\ncase object SearchButtonsCategory extends Category {\n  override def name: String = \"SEARCH BUTTONS\"\n}\n\ncase object SliderOptionCategory extends Category {\n  override def name: String = \"SLIDER OPTION\"\n}\n\ncase object SubscriptionCategory extends Category {\n  override def name: String = \"SUBSCRIPTION\"\n}\n\ncase object TopBarCategory extends Category {\n  override def name: String = \"TOP BAR\"\n}\n\ncase object WizardStartCategory extends Category {\n  override def name: String = \"WIZARD START\"\n}\n\ncase object WizardConfigurationCategory extends Category {\n  override def name: String = \"WIZARD CONFIGURATION\"\n}\n\ncase object WizardCollectionsCategory extends Category {\n  override def name: String = \"WIZARD COLLECTIONS\"\n}\n\ncase object WizardMomentsWifiCategory extends Category {\n  override def name: String = \"WIZARD MOMENTS WIFI\"\n}\n\ncase object WizardOtherMomentsCategory extends Category {\n  override def name: String = \"WIZARD OTHER MOMENTS\"\n}\n\ncase object WorkSpaceCategory extends Category {\n  override def name: String = \"WORKSPACE\"\n}\n\ncase object WorkSpaceActionsCategory extends Category {\n  override def name: String = \"WORKSPACE ACTIONS\"\n}\n\ncase object WorkSpaceBottomActionsCategory extends Category {\n  override def name: String = \"WORKSPACE BOTTOM ACTIONS\"\n}\n\ncase object WorkSpaceDragAndDropCategory extends Category {\n  override def name: String = \"WORKSPACE DRAG AND DROP\"\n}\n\ncase object WorkSpaceGestureActionsCategory extends Category {\n  override def name: String = \"WORKSPACE GESTURE ACTIONS\"\n}\n\ncase object WorkSpaceLinkReceived extends Category {\n  override def name: String = \"WORKSPACE LINK RECEIVED\"\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/CollectionType.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait CollectionType {\n  val name: String\n}\n\ncase object AppsCollectionType extends CollectionType {\n  override val name: String = \"APPS\"\n}\n\ncase object ContactsCollectionType extends CollectionType {\n  override val name: String = \"CONTACTS\"\n}\n\ncase object MomentCollectionType extends CollectionType {\n  override val name: String = \"MOMENT\"\n}\n\ncase object FreeCollectionType extends CollectionType {\n  override val name: String = \"FREE\"\n}\n\nobject CollectionType {\n\n  val collectionTypes =\n    Seq(AppsCollectionType, ContactsCollectionType, MomentCollectionType, FreeCollectionType)\n\n  def apply(name: String): CollectionType =\n    collectionTypes find (_.name == name) getOrElse FreeCollectionType\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/ConditionWeather.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait ConditionWeather\n\ncase object ClearCondition extends ConditionWeather\n\ncase object CloudyCondition extends ConditionWeather\n\ncase object FoggyCondition extends ConditionWeather\n\ncase object HazyCondition extends ConditionWeather\n\ncase object IcyCondition extends ConditionWeather\n\ncase object RainyCondition extends ConditionWeather\n\ncase object SnowyCondition extends ConditionWeather\n\ncase object StormyCondition extends ConditionWeather\n\ncase object WindyCondition extends ConditionWeather\n\ncase object UnknownCondition extends ConditionWeather\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/ContactsFilter.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait ContactsFilter\n\ncase object AllContacts extends ContactsFilter\n\ncase object FavoriteContacts extends ContactsFilter\n\ncase object ContactsWithPhoneNumber extends ContactsFilter\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/DialogToolbarType.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait DialogToolbarType\n\ncase object DialogToolbarTitle extends DialogToolbarType\n\ncase object DialogToolbarSearch extends DialogToolbarType\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/DockType.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait DockType {\n  val name: String\n}\n\ncase object AppDockType extends DockType {\n  override val name: String = \"APP\"\n}\n\ncase object CollectionDockType extends DockType {\n  override val name: String = \"COLLECTION\"\n}\n\ncase object ContactDockType extends DockType {\n  override val name: String = \"CONTACT\"\n}\n\nobject DockType {\n\n  val dockTypes = Seq(AppDockType, CollectionDockType, ContactDockType)\n\n  def apply(name: String): DockType =\n    dockTypes find (_.name == name) getOrElse\n      (throw new IllegalArgumentException(s\"$name not found\"))\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/EmailCategory.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait EmailCategory\n\ncase object EmailHome extends EmailCategory\n\ncase object EmailWork extends EmailCategory\n\ncase object EmailOther extends EmailCategory\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/FetchAppOrder.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait FetchAppOrder\n\ncase object OrderByName extends FetchAppOrder\n\ncase object OrderByInstallDate extends FetchAppOrder\n\ncase object OrderByCategory extends FetchAppOrder\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/GetAppOrder.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait GetAppOrder {\n  val ascending: Boolean\n  val name: String\n}\n\ncase class GetByName(ascending: Boolean) extends GetAppOrder {\n  override val name: String = \"GET BY NAME\"\n}\n\nobject GetByName extends GetByName(true)\n\ncase class GetByInstallDate(ascending: Boolean) extends GetAppOrder {\n  override val name: String = \"GET BY INSTALL DATE\"\n}\n\nobject GetByInstallDate extends GetByInstallDate(false)\n\ncase class GetByCategory(ascending: Boolean) extends GetAppOrder {\n  override val name: String = \"GET BY CATEGORY\"\n}\n\nobject GetByCategory extends GetByCategory(true)\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/KindActivity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait KindActivity\n\ncase object InVehicleActivity extends KindActivity\n\ncase object OnBicycleActivity extends KindActivity\n\ncase object OnFootActivity extends KindActivity\n\ncase object RunningActivity extends KindActivity\n\ncase object StillActivity extends KindActivity\n\ncase object TiltingActivity extends KindActivity\n\ncase object WalkingActivity extends KindActivity\n\ncase object UnknownActivity extends KindActivity\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/Label.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait Label {\n  def name: String\n}\n\ncase class ProvideLabel(label: String) extends Label {\n  override def name: String = label\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/NineCardsCategory.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait NineCardsCategory {\n  val name: String\n\n  def getStringResource: String = name.toLowerCase\n  def getIconResource: String   = name.toLowerCase\n  def isCustomCategory          = NineCardsCategory.customCategories contains this\n  def isAppCategory             = NineCardsCategory.appsCategories contains this\n  def isGameCategory            = NineCardsCategory.gamesCategories contains this\n\n  def toAppCategory: NineCardsCategory = this match {\n    case c if c.isGameCategory => Game\n    case c                     => c\n  }\n}\n\ncase object AllAppsCategory extends NineCardsCategory {\n  override val name: String = \"ALL_APPS\"\n}\n\ncase object AllCategories extends NineCardsCategory {\n  override val name: String = \"ALL_CATEGORIES\"\n}\n\ncase object ArtAndDesign extends NineCardsCategory {\n  override val name: String = \"ART_AND_DESIGN\"\n}\n\ncase object AutoAndVehicles extends NineCardsCategory {\n  override val name: String = \"AUTO_AND_VEHICLES\"\n}\n\ncase object Beauty extends NineCardsCategory {\n  override val name: String = \"BEAUTY\"\n}\n\ncase object BooksAndReference extends NineCardsCategory {\n  override val name: String = \"BOOKS_AND_REFERENCE\"\n}\n\ncase object Business extends NineCardsCategory {\n  override val name: String = \"BUSINESS\"\n}\n\ncase object Comics extends NineCardsCategory {\n  override val name: String = \"COMICS\"\n}\n\ncase object Communication extends NineCardsCategory {\n  override val name: String = \"COMMUNICATION\"\n}\n\ncase object ContactsCategory extends NineCardsCategory {\n  override val name: String = \"CONTACTS\"\n}\n\ncase object Dating extends NineCardsCategory {\n  override val name: String = \"DATING\"\n}\n\ncase object Education extends NineCardsCategory {\n  override val name: String = \"EDUCATION\"\n}\n\ncase object Entertainment extends NineCardsCategory {\n  override val name: String = \"ENTERTAINMENT\"\n}\n\ncase object Events extends NineCardsCategory {\n  override val name: String = \"EVENTS\"\n}\n\ncase object Finance extends NineCardsCategory {\n  override val name: String = \"FINANCE\"\n}\n\ncase object FoodAndDrink extends NineCardsCategory {\n  override val name: String = \"FOOD_AND_DRINK\"\n}\n\ncase object Game extends NineCardsCategory {\n  override val name: String = \"GAME\"\n}\n\ncase object HealthAndFitness extends NineCardsCategory {\n  override val name: String = \"HEALTH_AND_FITNESS\"\n}\n\ncase object HouseAndHome extends NineCardsCategory {\n  override val name: String = \"HOUSE_AND_HOME\"\n}\n\ncase object LibrariesAndDemo extends NineCardsCategory {\n  override val name: String = \"LIBRARIES_AND_DEMO\"\n}\n\ncase object Lifestyle extends NineCardsCategory {\n  override val name: String = \"LIFESTYLE\"\n}\n\ncase object MapsAndNavigation extends NineCardsCategory {\n  override val name: String = \"MAPS_AND_NAVIGATION\"\n}\n\ncase object Medical extends NineCardsCategory {\n  override val name: String = \"MEDICAL\"\n}\n\ncase object Misc extends NineCardsCategory {\n  override val name: String = \"MISC\"\n}\n\ncase object MusicAndAudio extends NineCardsCategory {\n  override val name: String = \"MUSIC_AND_AUDIO\"\n}\n\ncase object NewsAndMagazines extends NineCardsCategory {\n  override val name: String = \"NEWS_AND_MAGAZINES\"\n}\n\ncase object Parenting extends NineCardsCategory {\n  override val name: String = \"PARENTING\"\n}\n\ncase object Personalization extends NineCardsCategory {\n  override val name: String = \"PERSONALIZATION\"\n}\n\ncase object Photography extends NineCardsCategory {\n  override val name: String = \"PHOTOGRAPHY\"\n}\n\ncase object Productivity extends NineCardsCategory {\n  override val name: String = \"PRODUCTIVITY\"\n}\n\ncase object Shopping extends NineCardsCategory {\n  override val name: String = \"SHOPPING\"\n}\n\ncase object Social extends NineCardsCategory {\n  override val name: String = \"SOCIAL\"\n}\n\ncase object Sports extends NineCardsCategory {\n  override val name: String = \"SPORTS\"\n}\n\ncase object Tools extends NineCardsCategory {\n  override val name: String = \"TOOLS\"\n}\n\ncase object TravelAndLocal extends NineCardsCategory {\n  override val name: String = \"TRAVEL_AND_LOCAL\"\n}\n\ncase object VideoPlayers extends NineCardsCategory {\n  override val name: String = \"VIDEO_PLAYERS\"\n}\n\ncase object Weather extends NineCardsCategory {\n  override val name: String = \"WEATHER\"\n}\n\ncase object GameAction extends NineCardsCategory {\n  override val name: String = \"GAME_ACTION\"\n}\n\ncase object GameArcade extends NineCardsCategory {\n  override val name: String = \"GAME_ARCADE\"\n}\n\ncase object GameAdventure extends NineCardsCategory {\n  override val name: String = \"GAME_ADVENTURE\"\n}\n\ncase object GameBoard extends NineCardsCategory {\n  override val name: String = \"GAME_BOARD\"\n}\n\ncase object GameCard extends NineCardsCategory {\n  override val name: String = \"GAME_CARD\"\n}\n\ncase object GameCasino extends NineCardsCategory {\n  override val name: String = \"GAME_CASINO\"\n}\n\ncase object GameCasual extends NineCardsCategory {\n  override val name: String = \"GAME_CASUAL\"\n}\n\ncase object GameEducational extends NineCardsCategory {\n  override val name: String = \"GAME_EDUCATIONAL\"\n}\n\ncase object GameMusic extends NineCardsCategory {\n  override val name: String = \"GAME_MUSIC\"\n}\n\ncase object GamePuzzle extends NineCardsCategory {\n  override val name: String = \"GAME_PUZZLE\"\n}\n\ncase object GameRacing extends NineCardsCategory {\n  override val name: String = \"GAME_RACING\"\n}\n\ncase object GameRolePlaying extends NineCardsCategory {\n  override val name: String = \"GAME_ROLE_PLAYING\"\n}\n\ncase object GameSimulation extends NineCardsCategory {\n  override val name: String = \"GAME_SIMULATION\"\n}\n\ncase object GameSports extends NineCardsCategory {\n  override val name: String = \"GAME_SPORTS\"\n}\n\ncase object GameStrategy extends NineCardsCategory {\n  override val name: String = \"GAME_STRATEGY\"\n}\n\ncase object GameTrivia extends NineCardsCategory {\n  override val name: String = \"GAME_TRIVIA\"\n}\n\ncase object GameWord extends NineCardsCategory {\n  override val name: String = \"GAME_WORD\"\n}\n\nobject NineCardsCategory {\n\n  val customCategories = Seq(AllAppsCategory, AllCategories, Misc, ContactsCategory)\n\n  val appsCategories = Seq(\n    ArtAndDesign,\n    AutoAndVehicles,\n    Beauty,\n    BooksAndReference,\n    Business,\n    Comics,\n    Communication,\n    Dating,\n    Education,\n    Entertainment,\n    Events,\n    Finance,\n    FoodAndDrink,\n    Game,\n    HealthAndFitness,\n    HouseAndHome,\n    LibrariesAndDemo,\n    Lifestyle,\n    MapsAndNavigation,\n    Medical,\n    MusicAndAudio,\n    NewsAndMagazines,\n    Parenting,\n    Personalization,\n    Photography,\n    Productivity,\n    Shopping,\n    Social,\n    Sports,\n    Tools,\n    TravelAndLocal,\n    VideoPlayers,\n    Weather)\n\n  val gamesCategories = Seq(\n    GameAction,\n    GameAdventure,\n    GameArcade,\n    GameBoard,\n    GameCard,\n    GameCasino,\n    GameCasual,\n    GameEducational,\n    GameMusic,\n    GamePuzzle,\n    GameRacing,\n    GameRolePlaying,\n    GameSimulation,\n    GameSports,\n    GameStrategy,\n    GameTrivia,\n    GameWord)\n\n  val allCategories = customCategories ++ gamesCategories ++ appsCategories\n\n  def apply(name: String): NineCardsCategory = allCategories find (_.name == name) getOrElse Misc\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/NineCardsMoment.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait NineCardsMoment {\n  val name: String\n  val isDefault: Boolean = false\n\n  def getIconResource: String   = name.toLowerCase\n  def getStringResource: String = name.toLowerCase\n}\n\ncase object HomeMorningMoment extends NineCardsMoment {\n  override val name: String = \"HOME\"\n}\n\ncase object WorkMoment extends NineCardsMoment {\n  override val name: String = \"WORK\"\n}\n\ncase object HomeNightMoment extends NineCardsMoment {\n  override val name: String = \"NIGHT\"\n}\n\ncase object StudyMoment extends NineCardsMoment {\n  override val name: String = \"STUDY\"\n}\n\ncase object MusicMoment extends NineCardsMoment {\n  override val name: String = \"MUSIC\"\n}\n\ncase object CarMoment extends NineCardsMoment {\n  override val name: String = \"CAR\"\n}\n\ncase object SportMoment extends NineCardsMoment {\n  override val name: String = \"SPORT\"\n}\n\ncase object OutAndAboutMoment extends NineCardsMoment {\n  override val name: String       = \"OUT_AND_ABOUT\"\n  override val isDefault: Boolean = true\n}\n\ncase class UnknownMoment(name: String) extends NineCardsMoment\n\nobject NineCardsMoment {\n\n  val activityMoments = Seq(CarMoment)\n\n  val hourlyMoments = Seq(HomeMorningMoment, WorkMoment, HomeNightMoment, StudyMoment, SportMoment)\n\n  val defaultMoment = OutAndAboutMoment\n\n  val moments = hourlyMoments ++ Seq(MusicMoment, defaultMoment) ++ activityMoments\n\n  def apply(name: String): NineCardsMoment =\n    moments find (_.name == name) getOrElse UnknownMoment(name)\n\n  def apply(maybeName: Option[String]): NineCardsMoment =\n    maybeName map apply getOrElse UnknownMoment(maybeName.getOrElse(\"\"))\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/Permission.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait PermissionStatus\n\ncase object PermissionGranted extends PermissionStatus\n\ncase object PermissionDenied extends PermissionStatus\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/PhoneCategory.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait PhoneCategory\n\ncase object PhoneHome extends PhoneCategory\n\ncase object PhoneWork extends PhoneCategory\n\ncase object PhoneMobile extends PhoneCategory\n\ncase object PhoneMain extends PhoneCategory\n\ncase object PhoneFaxWork extends PhoneCategory\n\ncase object PhoneFaxHome extends PhoneCategory\n\ncase object PhonePager extends PhoneCategory\n\ncase object PhoneOther extends PhoneCategory\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/PublicCollectionStatus.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait PublicCollectionStatus\n\ncase object NotPublished extends PublicCollectionStatus\n\ncase object PublishedByMe extends PublicCollectionStatus\n\ncase object PublishedByOther extends PublicCollectionStatus\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/Screen.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait Screen {\n  def name: String\n}\n\ncase object AppDrawerScreen extends Screen {\n  override def name: String = \"AppDrawer\"\n}\n\ncase object CollectionDetailScreen extends Screen {\n  override def name: String = \"CollectionDetail\"\n}\n\ncase object HomeScreen extends Screen {\n  override def name: String = \"Home\"\n}\n\ncase object LauncherScreen extends Screen {\n  override def name: String = \"Launcher\"\n}\n\ncase object MomentsScreen extends Screen {\n  override def name: String = \"Moments\"\n}\n\ncase object ProfileScreen extends Screen {\n  override def name: String = \"Profile\"\n}\n\ncase object SliderMenuScreen extends Screen {\n  override def name: String = \"SliderMenu\"\n}\n\ncase object WidgetScreen extends Screen {\n  override def name: String = \"Widget\"\n}\n\ncase object WizardScreen extends Screen {\n  override def name: String = \"Wizard\"\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/TypeSharedCollection.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait TypeSharedCollection {\n  val name: String\n}\n\ncase object TopSharedCollection extends TypeSharedCollection {\n  override val name: String = \"top\"\n}\n\ncase object LatestSharedCollection extends TypeSharedCollection {\n  override val name: String = \"latest\"\n}\n\nobject TypeSharedCollection {\n\n  val cases = Seq(TopSharedCollection, LatestSharedCollection)\n\n  def apply(name: String): TypeSharedCollection =\n    cases find (_.name == name) getOrElse\n      (throw new IllegalArgumentException(s\"$name not found\"))\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/Value.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait Value {\n  def value: Long\n}\n\ncase object NoValue extends Value {\n  override def value: Long = 0\n}\n\ncase object VeryLowValue extends Value {\n  override def value: Long = 1\n}\n\ncase object LowValue extends Value {\n  override def value: Long = 2\n}\n\ncase object MediumValue extends Value {\n  override def value: Long = 3\n}\n\ncase object HighValue extends Value {\n  override def value: Long = 4\n}\n\ncase object VeryHighValue extends Value {\n  override def value: Long = 5\n}\n\ncase class ProvideValue(v: Long) extends Value {\n  override def value: Long = v\n}\n\n// Values related to manage apps\n\ncase object OpenAppFromCollectionValue extends Value {\n  override def value: Long = 3\n}\n\ncase object OpenAppFromAppDrawerValue extends Value {\n  override def value: Long = 1\n}\n\ncase object OpenAppFromDockValue extends Value {\n  override def value: Long = 1\n}\n\ncase object AddedToCollectionValue extends Value {\n  override def value: Long = 10\n}\n\ncase object RemovedFromCollectionValue extends Value {\n  override def value: Long = -3\n}\n\ncase object OpenMomentFromWorkspaceValue extends Value {\n  override def value: Long = 3\n}\n\n// Values related to widget\n\ncase object AddedWidgetToMomentValue extends Value {\n  override def value: Long = 10\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/WidgetResizeMode.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nimport android.appwidget.AppWidgetProviderInfo\n\nsealed trait WidgetResizeMode\n\ncase object WidgetResizeNone extends WidgetResizeMode\n\ncase object WidgetResizeVertical extends WidgetResizeMode\n\ncase object WidgetResizeHorizontal extends WidgetResizeMode\n\ncase object WidgetResizeBoth extends WidgetResizeMode\n\nobject WidgetResizeMode {\n\n  def apply(mode: Int): WidgetResizeMode = mode match {\n    case AppWidgetProviderInfo.RESIZE_VERTICAL   => WidgetResizeVertical\n    case AppWidgetProviderInfo.RESIZE_HORIZONTAL => WidgetResizeHorizontal\n    case AppWidgetProviderInfo.RESIZE_BOTH       => WidgetResizeBoth\n    case _                                       => WidgetResizeNone\n  }\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/WidgetType.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types\n\nsealed trait WidgetType {\n  val name: String\n}\n\ncase object AppWidgetType extends WidgetType {\n  override val name: String = \"APP\"\n}\n\nobject WidgetType {\n\n  val widgetTypes = Seq(AppWidgetType)\n\n  def apply(name: String): WidgetType =\n    widgetTypes find (_.name == name) getOrElse\n      (throw new IllegalArgumentException(s\"$name not found\"))\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/json/CollectionTypeImplicits.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types.json\n\nimport cards.nine.models.types.CollectionType\nimport play.api.libs.json._\n\nobject CollectionTypeImplicits {\n\n  implicit val collectionTypeReads = new Reads[CollectionType] {\n    def reads(js: JsValue): JsResult[CollectionType] =\n      JsSuccess(CollectionType(js.as[String]))\n  }\n\n  implicit val collectionTypeWrites = new Writes[CollectionType] {\n    def writes(collectionType: CollectionType): JsValue =\n      JsString(collectionType.name)\n  }\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/json/DockTypeImplicits.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types.json\n\nimport cards.nine.models.types.DockType\nimport play.api.libs.json._\n\nobject DockTypeImplicits {\n\n  implicit val dockTypeReads = new Reads[DockType] {\n    def reads(js: JsValue): JsResult[DockType] =\n      JsSuccess(DockType(js.as[String]))\n  }\n\n  implicit val dockTypeWrites = new Writes[DockType] {\n    def writes(dockType: DockType): JsValue =\n      JsString(dockType.name)\n  }\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/json/NineCardCategoryImplicits.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types.json\n\nimport cards.nine.models.types.NineCardsCategory\nimport play.api.libs.json._\n\nobject NineCardCategoryImplicits {\n\n  implicit val nineCardCategoryReads = new Reads[NineCardsCategory] {\n    def reads(js: JsValue): JsResult[NineCardsCategory] =\n      JsSuccess(NineCardsCategory(js.as[String]))\n  }\n\n  implicit val nineCardCategoryWrites = new Writes[NineCardsCategory] {\n    def writes(category: NineCardsCategory): JsValue =\n      JsString(category.name)\n  }\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/json/NineCardsMomentImplicits.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types.json\n\nimport cards.nine.models.types.NineCardsMoment\nimport play.api.libs.json._\n\nobject NineCardsMomentImplicits {\n\n  implicit val nineCardMomentReads = new Reads[NineCardsMoment] {\n    def reads(js: JsValue): JsResult[NineCardsMoment] =\n      JsSuccess(NineCardsMoment(js.as[String]))\n  }\n\n  implicit val nineCardMomentWrites = new Writes[NineCardsMoment] {\n    def writes(moment: NineCardsMoment): JsValue =\n      JsString(moment.name)\n  }\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/json/WidgetTypeImplicits.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types.json\n\nimport cards.nine.models.types.WidgetType\nimport play.api.libs.json._\n\nobject WidgetTypeImplicits {\n\n  implicit val widgetTypeReads = new Reads[WidgetType] {\n    def reads(js: JsValue): JsResult[WidgetType] =\n      JsSuccess(WidgetType(js.as[String]))\n  }\n\n  implicit val widgetTypeWrites = new Writes[WidgetType] {\n    def writes(widgetType: WidgetType): JsValue =\n      JsString(widgetType.name)\n  }\n\n}\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/theme/ThemeStyleType.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types.theme\n\nsealed trait ThemeStyleType\n\ncase object PrimaryColor extends ThemeStyleType\n\ncase object DockPressedColor extends ThemeStyleType\n\ncase object CardLayoutBackgroundColor extends ThemeStyleType\n\ncase object CardBackgroundColor extends ThemeStyleType\n\ncase object CardBackgroundPressedColor extends ThemeStyleType\n\ncase object CardTextColor extends ThemeStyleType\n\ncase object CollectionDetailTextTabDefaultColor extends ThemeStyleType\n\ncase object CollectionDetailTextTabSelectedColor extends ThemeStyleType\n\ncase object DrawerTabsBackgroundColor extends ThemeStyleType\n\ncase object DrawerBackgroundColor extends ThemeStyleType\n\ncase object DrawerTextColor extends ThemeStyleType\n\ncase object SearchBackgroundColor extends ThemeStyleType\n\ncase object SearchGoogleColor extends ThemeStyleType\n\ncase object SearchIconsColor extends ThemeStyleType\n\ncase object SearchTextColor extends ThemeStyleType\n\ncase object SearchPressedColor extends ThemeStyleType\n\ncase object DrawerIconColor extends ThemeStyleType\n"
  },
  {
    "path": "modules/models/src/main/scala/cards/nine/models/types/theme/ThemeType.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.models.types.theme\n\nsealed trait ThemeType\n\ncase object ThemeLight extends ThemeType\n\ncase object ThemeDark extends ThemeType\n"
  },
  {
    "path": "modules/process/build.sbt",
    "content": "platformTarget in Android := \"android-24\"\n"
  },
  {
    "path": "modules/process/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      package=\"cards.nine.process\"\n      android:versionCode=\"1\"\n      android:versionName=\"1.0\">\n    <uses-sdk\n        android:minSdkVersion=\"16\"\n        android:targetSdkVersion=\"24\"/>\n\n    <application />\n</manifest>\n"
  },
  {
    "path": "modules/process/src/main/assets/theme_dark.json",
    "content": "\n{\n  \"name\": \"dark\",\n  \"parent\": \"dark\",\n  \"styles\": [\n    {\n      \"styleType\": \"PrimaryColor\",\n      \"color\": \"#FF9800\"\n    },\n    {\n      \"styleType\": \"SearchBackgroundColor\",\n      \"color\": \"#37474F\"\n    },\n    {\n      \"styleType\": \"SearchPressedColor\",\n      \"color\": \"#dddddd\"\n    },\n    {\n      \"styleType\": \"SearchGoogleColor\",\n      \"color\": \"#eeeeee\"\n    },\n    {\n      \"styleType\": \"SearchIconsColor\",\n      \"color\": \"#deffffff\"\n    },\n    {\n      \"styleType\": \"SearchTextColor\",\n      \"color\": \"#ffffff\"\n    },\n    {\n      \"styleType\": \"DrawerTabsBackgroundColor\",\n      \"color\": \"#16ffffff\"\n    },\n    {\n      \"styleType\": \"DrawerBackgroundColor\",\n      \"color\": \"#37474F\"\n    },\n    {\n      \"styleType\": \"DrawerTextColor\",\n      \"color\": \"#deffffff\"\n    },\n    {\n      \"styleType\": \"DrawerIconColor\",\n      \"color\": \"#deffffff\"\n    },\n    {\n      \"styleType\": \"DockPressedColor\",\n      \"color\": \"#ffd5f2fa\"\n    },\n    {\n      \"styleType\": \"CardLayoutBackgroundColor\",\n      \"color\": \"#37474F\"\n    },\n    {\n      \"styleType\": \"CardTextColor\",\n      \"color\": \"#deffffff\"\n    },\n    {\n      \"styleType\": \"CardBackgroundColor\",\n      \"color\": \"#455A64\"\n    },\n    {\n      \"styleType\": \"CardBackgroundPressedColor\",\n      \"color\": \"#ffffff\"\n    },\n    {\n      \"styleType\": \"CollectionDetailTextTabSelectedColor\",\n      \"color\": \"#ffffff\"\n    },\n    {\n      \"styleType\": \"CollectionDetailTextTabDefaultColor\",\n      \"color\": \"#80ffffff\"\n    }\n  ],\n  \"themeColors\": {\n    \"defaultColor\": \"#78909C\",\n    \"colors\": [\"#FFA726\", \"#EF5350\", \"#AB47BC\", \"#42A5F5\", \"#7E57C2\", \"#5C6BC0\", \"#9CCC65\", \"#66BB6A\", \"#26A69A\"]\n  }\n}"
  },
  {
    "path": "modules/process/src/main/assets/theme_light.json",
    "content": "{\n  \"name\": \"light\",\n  \"parent\": \"light\",\n  \"styles\": [\n    {\n      \"styleType\": \"PrimaryColor\",\n      \"color\": \"#3F51B5\"\n    },\n    {\n      \"styleType\": \"SearchBackgroundColor\",\n      \"color\": \"#ffffff\"\n    },\n    {\n      \"styleType\": \"SearchPressedColor\",\n      \"color\": \"#ff59afdd\"\n    },\n    {\n      \"styleType\": \"SearchGoogleColor\",\n      \"color\": \"#a3a3a3\"\n    },\n    {\n      \"styleType\": \"SearchIconsColor\",\n      \"color\": \"#646464\"\n    },\n    {\n      \"styleType\": \"SearchTextColor\",\n      \"color\": \"#646464\"\n    },\n    {\n      \"styleType\": \"DrawerTabsBackgroundColor\",\n      \"color\": \"#16000000\"\n    },\n    {\n      \"styleType\": \"DrawerBackgroundColor\",\n      \"color\": \"#ffffff\"\n    },\n    {\n      \"styleType\": \"DrawerTextColor\",\n      \"color\": \"#cc000000\"\n    },\n    {\n      \"styleType\": \"DrawerIconColor\",\n      \"color\": \"#99000000\"\n    },\n    {\n      \"styleType\": \"DockPressedColor\",\n      \"color\": \"#ffd5f2fa\"\n    },\n    {\n      \"styleType\": \"CardLayoutBackgroundColor\",\n      \"color\": \"#eeeeee\"\n    },\n    {\n      \"styleType\": \"CardTextColor\",\n      \"color\": \"#000000\"\n    },\n    {\n      \"styleType\": \"CardBackgroundColor\",\n      \"color\": \"#ffffff\"\n    },\n    {\n      \"styleType\": \"CardBackgroundPressedColor\",\n      \"color\": \"#000000\"\n    },\n    {\n      \"styleType\": \"CollectionDetailTextTabSelectedColor\",\n      \"color\": \"#ffffff\"\n    },\n    {\n      \"styleType\": \"CollectionDetailTextTabDefaultColor\",\n      \"color\": \"#80ffffff\"\n    }\n  ],\n  \"themeColors\": {\n    \"defaultColor\": \"#607D8B\",\n    \"colors\": [\"#FF9800\", \"#F44336\", \"#9C27B0\", \"#2196F3\", \"#673AB7\", \"#3F51B5\", \"#8BC34A\", \"#4CAF50\", \"#009688\"]\n  }\n}"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/accounts/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.accounts\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ntrait UserAccountsProcessException extends NineCardException\n\ncase class UserAccountsProcessExceptionImpl(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with UserAccountsProcessException {\n  cause map initCause\n}\n\ncase class UserAccountsProcessPermissionException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with UserAccountsProcessException {\n  cause map initCause\n}\n\ncase class UserAccountsProcessOperationCancelledException(\n    message: String,\n    cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with UserAccountsProcessException {\n  cause map initCause\n}\n\ntrait ImplicitsAccountsProcessExceptions {\n\n  implicit def accountsServicesExceptionConverter =\n    (t: Throwable) => UserAccountsProcessExceptionImpl(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/accounts/UserAccountsProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.accounts\n\nimport cards.nine.commons.contexts.{ActivityContextSupport, ContextSupport}\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.types.{AppPermission, PermissionResult}\n\ntrait UserAccountsProcess {\n\n  /**\n   * Get the Google accounts in the device.\n   *\n   * @return a sequence of accounts\n   * @throws UserAccountsProcessPermissionException if the user didn't grant permission for reading the accounts\n   * @throws UserAccountsProcessException           if the service found a problem getting the accounts\n   */\n  def getGoogleAccounts(implicit contextSupport: ContextSupport): TaskService[Seq[String]]\n\n  /**\n   * Get the auth token associated to the specified account and token\n   *\n   * @param accountName the account email\n   * @param scope the scope\n   * @return the token\n   * @throws UserAccountsProcessOperationCancelledException if the user cancelled the token request\n   * @throws UserAccountsProcessException                   if the service found a problem getting the token\n   */\n  def getAuthToken(accountName: String, scope: String)(\n      implicit contextSupport: ActivityContextSupport): TaskService[String]\n\n  /**\n   * Invalidates the token\n   *\n   * @param token the token to invalidate\n   * @throws UserAccountsProcessException if the service found a problem invalidating the token\n   */\n  def invalidateToken(token: String)(implicit contextSupport: ContextSupport): TaskService[Unit]\n\n  /**\n   * Verifies if the app has the permission\n   *\n   * @param permission the permission\n   * @return a PermissionResult indicating the status of the permission\n   * @throws UserAccountsProcessException  if the service found a problem checking the permission\n   */\n  def havePermission(permission: AppPermission)(\n      implicit contextSupport: ContextSupport): TaskService[PermissionResult]\n\n  /**\n   * Verifies if the app has some permissions\n   *\n   * @param permissions the permissions\n   * @return a Seq[PermissionResult] indicating the status of each permission\n   * @throws UserAccountsProcessException  if the service found a problem checking the permission\n   */\n  def havePermissions(permissions: Seq[AppPermission])(\n      implicit contextSupport: ContextSupport): TaskService[Seq[PermissionResult]]\n\n  /**\n   * Check if we should ask for the permission\n   *\n   * @param permission the permission\n   * @return a PermissionResult indicating the status of the request\n   * @throws UserAccountsProcessException  if the service found a problem\n   */\n  def shouldRequestPermission(permission: AppPermission)(\n      implicit contextSupport: ActivityContextSupport): TaskService[PermissionResult]\n\n  /**\n   * Check if we should ask for the permissions\n   *\n   * @param permissions the permissions\n   * @return a Seq[PermissionResult] indicating the status of the request\n   * @throws UserAccountsProcessException  if the service found a problem\n   */\n  def shouldRequestPermissions(permissions: Seq[AppPermission])(\n      implicit contextSupport: ActivityContextSupport): TaskService[Seq[PermissionResult]]\n\n  /**\n   * Try to request the permission with the Activity passed inside the contextSupport\n   *\n   * @param requestCode the permission request code\n   * @param permission the permission\n   * @throws UserAccountsProcessException  if the service found a problem\n   */\n  def requestPermission(requestCode: Int, permission: AppPermission)(\n      implicit contextSupport: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Try to request the permissions with the Activity passed inside the contextSupport\n   *\n   * @param requestCode the permission request code\n   * @param permissions the permissions\n   * @throws UserAccountsProcessException  if the service found a problem\n   */\n  def requestPermissions(requestCode: Int, permissions: Seq[AppPermission])(\n      implicit contextSupport: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Parses the response of the permission request\n   *\n   * @param permissions the permissions names\n   * @param grantResults the status received from the request\n   * @return a Seq[PermissionResult] indicating the status of the request\n   * @throws UserAccountsProcessException  if the service found a problem\n   */\n  def parsePermissionsRequestResult(\n      permissions: Array[String],\n      grantResults: Array[Int]): TaskService[Seq[PermissionResult]]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/accounts/impl/UserAccountsProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.accounts.impl\n\nimport android.accounts.{\n  Account,\n  AccountManager,\n  AccountManagerCallback,\n  AccountManagerFuture,\n  OperationCanceledException\n}\nimport android.app.Activity\nimport android.os.Bundle\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons._\nimport cards.nine.commons.contexts.{ActivityContextSupport, ContextSupport}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.{\n  AppPermission,\n  PermissionDenied,\n  PermissionGranted,\n  PermissionResult\n}\nimport cards.nine.process.accounts.{UserAccountsProcessException, _}\nimport cards.nine.services.permissions.PermissionsServices\nimport cats.syntax.either._\nimport monix.eval.{Callback, Task}\nimport monix.execution.Cancelable\n\nimport scala.util.{Failure, Success, Try}\n\nclass UserAccountsProcessImpl(permissionsServices: PermissionsServices)\n    extends UserAccountsProcess\n    with ImplicitsAccountsProcessExceptions {\n\n  val googleAccountType = \"com.google\"\n\n  override def getGoogleAccounts(implicit contextSupport: ContextSupport) = {\n\n    def safeNullArrayToSeq[T](array: Array[T]): Seq[T] =\n      Option(array) map (_.toSeq) getOrElse Seq.empty\n\n    def getAccounts: Seq[Account] =\n      safeNullArrayToSeq(contextSupport.getAccountManager.getAccountsByType(googleAccountType))\n\n    TaskService {\n      Task {\n        Either.catchNonFatal(getAccounts map (_.name)) leftMap {\n          case e: SecurityException =>\n            UserAccountsProcessPermissionException(e.getMessage, Some(e))\n          case t: Throwable => UserAccountsProcessExceptionImpl(t.getMessage, Some(t))\n        }\n      }\n    }\n  }\n\n  override def getAuthToken(accountName: String, scope: String)(\n      implicit contextSupport: ActivityContextSupport) = {\n\n    def readAuthToken(\n        callback: Callback[Either[UserAccountsProcessException, String]],\n        activity: Activity): Unit = {\n      val androidAccount = new Account(accountName, googleAccountType)\n      contextSupport.getAccountManager.getAuthToken(\n        androidAccount,\n        scope,\n        javaNull,\n        activity,\n        new AccountManagerCallback[Bundle] {\n          override def run(future: AccountManagerFuture[Bundle]): Unit = {\n            Try {\n              val bundle = future.getResult\n              Option(bundle.getString(AccountManager.KEY_AUTHTOKEN)) getOrElse (throw new RuntimeException(\n                \"Received null token\"))\n            } match {\n              case Success(token) =>\n                callback(Success(Right(token)))\n              case Failure(e: OperationCanceledException) =>\n                callback(\n                  Success(\n                    Left(UserAccountsProcessOperationCancelledException(e.getMessage, Some(e)))))\n              case Failure(e: SecurityException) =>\n                callback(\n                  Success(Left(UserAccountsProcessPermissionException(e.getMessage, Some(e)))))\n              case Failure(e) =>\n                callback(Success(Left(UserAccountsProcessExceptionImpl(e.getMessage, Some(e)))))\n            }\n          }\n        },\n        javaNull)\n    }\n\n    TaskService {\n      Task.async[UserAccountsProcessException Either String] { (scheduler, callback) =>\n        contextSupport.getActivity match {\n          case Some(activity) => readAuthToken(callback, activity)\n          case None =>\n            callback(\n              Success(Left(UserAccountsProcessExceptionImpl(\"Activity instance is null\", None))))\n        }\n        Cancelable.empty\n      }\n    }\n  }\n\n  override def invalidateToken(token: String)(implicit contextSupport: ContextSupport) =\n    TaskService {\n      CatchAll[UserAccountsProcessExceptionImpl](\n        contextSupport.getAccountManager.invalidateAuthToken(googleAccountType, token))\n    }\n\n  override def havePermission(permission: AppPermission)(implicit contextSupport: ContextSupport) =\n    havePermissions(Seq(permission)) map (findPermissionOrFalse(permission, _))\n\n  override def havePermissions(permissions: Seq[AppPermission])(\n      implicit contextSupport: ContextSupport) =\n    permissionsServices\n      .checkPermissions(permissions.map(_.value))\n      .map { result =>\n        permissions map { permission =>\n          val permissionResult = result.getOrElse(permission.value, PermissionDenied)\n          PermissionResult(permission, permissionResult == PermissionGranted)\n        }\n      }\n      .resolve[UserAccountsProcessExceptionImpl]\n\n  override def shouldRequestPermission(permission: AppPermission)(\n      implicit contextSupport: ActivityContextSupport) =\n    shouldRequestPermissions(Seq(permission)) map (findPermissionOrFalse(permission, _))\n\n  override def shouldRequestPermissions(permissions: Seq[AppPermission])(\n      implicit contextSupport: ActivityContextSupport) =\n    permissionsServices.shouldShowRequestPermissions(permissions.map(_.value)).map { result =>\n      permissions map { permission =>\n        PermissionResult(permission, result.getOrElse(permission.value, false))\n      }\n    }\n\n  override def requestPermission(permissionRequestCode: Int, permission: AppPermission)(\n      implicit contextSupport: ActivityContextSupport) =\n    requestPermissions(permissionRequestCode, Seq(permission))\n\n  override def requestPermissions(permissionRequestCode: Int, permissions: Seq[AppPermission])(\n      implicit contextSupport: ActivityContextSupport) =\n    permissionsServices\n      .requestPermissions(permissionRequestCode, permissions.map(_.value))\n      .resolve[UserAccountsProcessExceptionImpl]\n\n  def parsePermissionsRequestResult(permissions: Array[String], grantResults: Array[Int]) = {\n\n    def parsePermission(value: String): Option[AppPermission] =\n      AppPermission.values.find(_.value == value)\n\n    permissionsServices.readPermissionsRequestResult(permissions.toSeq, grantResults.toSeq).map {\n      result =>\n        permissions flatMap { permission =>\n          parsePermission(permission) map (p =>\n                                             PermissionResult(\n                                               p,\n                                               result\n                                                 .getOrElse(p.value, PermissionDenied) == PermissionGranted))\n        }\n    }\n  }\n\n  private[this] def findPermissionOrFalse(\n      permission: AppPermission,\n      permissions: Seq[PermissionResult]): PermissionResult =\n    permissions.find(_.permission == permission) match {\n      case Some(result) => result\n      case None         => PermissionResult(permission, result = false)\n    }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/collection/CollectionProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.collection\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models._\nimport cards.nine.models.types.{NineCardsCategory, NineCardsMoment}\n\ntrait CollectionProcess {\n\n  /**\n   * Generate Private Collections with the apps installed in the device and their categories\n   *\n   * @param apps the Seq[cards.nine.models.ApplicationData] with the apps' data\n   * @return the Seq[cards.nine.models.CollectionData]\n   * @throws CollectionException if there was an error creating the existing collections\n   */\n  def generatePrivateCollections(apps: Seq[ApplicationData])(\n      implicit context: ContextSupport): TaskService[Seq[CollectionData]]\n\n  /**\n   * Creates Collections from some already formed and given Collections\n   *\n   * @param items the Seq[cards.nine.models.CollectionData] of Collections\n   * @return the List[cards.nine.models.Collection]\n   * @throws CollectionException if there was an error creating the collections\n   */\n  def createCollectionsFromCollectionData(items: Seq[CollectionData])(\n      implicit context: ContextSupport): TaskService[Seq[Collection]]\n\n  /**\n   * Gets the existing collections\n   *\n   * @return the Seq[cards.nine.models.Collection]\n   * @throws CollectionException if there was an error getting the existing collections\n   */\n  def getCollections: TaskService[Seq[Collection]]\n\n  /**\n   * Get collection by collection id if exists\n   *\n   * @return the Option[cards.nine.models.Collection]\n   * @throws CollectionException if there was an error getting the existing collections\n   */\n  def getCollectionById(id: Int): TaskService[Option[Collection]]\n\n  /**\n   * Get collection by category if exists\n   *\n   * @param category category of collection\n   * @return the Option[cards.nine.models.Collection]\n   * @throws CollectionException if there was an error getting the existing collections\n   */\n  def getCollectionByCategory(category: NineCardsCategory): TaskService[Option[Collection]]\n\n  /**\n   * Get collection by his shared collection id if exists\n   *\n   * @param sharedCollectionId the shared collection id\n   * @return the Option[cards.nine.models.Collection]\n   * @throws CollectionException if there was an error getting the existing collections\n   */\n  def getCollectionBySharedCollectionId(\n      sharedCollectionId: String): TaskService[Option[Collection]]\n\n  /**\n   * Adds a new Collection after the last existing one\n   *\n   * @param collection includes the necessary data to create a new collection (name, collectionType, icon, themedColorIndex and appsCategory(optional))\n   * @return the [[Collection]]\n   * @throws CollectionException if there was an error getting the existing collections or adding the new one\n   */\n  def addCollection(collection: CollectionData): TaskService[Collection]\n\n  /**\n   * Deletes a Collection and updates the position of the other Collections\n   *\n   * @param collectionId the Id of the Collection\n   * @throws CollectionException if there was an error finding the collection, getting the existing collections, deleting the collection or updating the rest of them\n   */\n  def deleteCollection(collectionId: Int): TaskService[Unit]\n\n  /**\n   * Deletes all Collections and Cards\n   *\n   * @throws CollectionException if there was an error finding the collection, getting the existing collections, deleting the collection or updating the rest of them\n   */\n  def cleanCollections(): TaskService[Unit]\n\n  /**\n   * Moves a Collection to another position and updates the position of the other Collections\n   *\n   * @param position the position of the Collection to move\n   * @param newPosition the new position of the Collection\n   * @throws CollectionException if there was an error finding the collection, getting the existing collections or updating the position of all the collections\n   */\n  def reorderCollection(position: Int, newPosition: Int): TaskService[Unit]\n\n  /**\n   * Edits a Collection and allows to change the name and the appsCategory of the Collection\n   *\n   * @param collectionId the Id of the Collection\n   * @param collection includes the data that can be edit in a collection (name, icon, themedColorIndex and appsCategory)\n   * @return the [[Collection]]\n   * @throws CollectionException if there was an error finding the collection or updating it\n   */\n  def editCollection(collectionId: Int, collection: CollectionData): TaskService[Collection]\n\n  /**\n   * Updates a Collection with the sharedCollectionId\n   *\n   * @param collectionId the Id of the Collection\n   * @param sharedCollectionId the Id of the collection after being published\n   * @return the [[Collection]]\n   * @throws CollectionException if there was an error finding the collection or updating it\n   */\n  def updateSharedCollection(\n      collectionId: Int,\n      sharedCollectionId: String): TaskService[Collection]\n\n  /**\n   * Adds some new packages to a given Collection\n   *\n   * @param collectionId the Id of the Collection\n   * @param packages the packages to be added to this collection\n   * @throws CardException if there was an error getting the existing cards or adding the new one\n   */\n  def addPackages(collectionId: Int, packages: Seq[String])(\n      implicit context: ContextSupport): TaskService[Unit]\n\n  /**\n   * Rank all the packages grouped by its category\n   *\n   * @return the Seq[cards.nine.models.PackagesByCategory] with the packages already ordered\n   * @throws CollectionException if there was an error getting the existing collections or getting the packages ordered\n   */\n  def rankApps()(implicit context: ContextSupport): TaskService[Seq[PackagesByCategory]]\n\n  /**\n   * Rank all the packages grouped by moment\n   *\n   * @param limit the maximum numbers of apps to order inside each moment\n   * @return the Seq[cards.nine.models.PackagesByMoment] with the packages already ordered\n   * @throws CollectionException if there was an error getting the existing collections or getting the packages ordered\n   */\n  def rankAppsByMoment(limit: Int)(\n      implicit context: ContextSupport): TaskService[Seq[PackagesByMoment]]\n\n  /**\n   * Rank all the widgets grouped by the given moment sequence\n   *\n   * @param limit the maximum numbers of widgets to order inside each moment\n   * @param moments the moments to order the widgets in\n   * @return the Seq[cards.nine.models.WidgetsByMoment] with the widgets already ordered\n   * @throws CollectionException if there was an error getting the existing collections or getting the packages ordered\n   */\n  def rankWidgetsByMoment(limit: Int, moments: Seq[NineCardsMoment])(\n      implicit context: ContextSupport): TaskService[Seq[WidgetsByMoment]]\n\n  /**\n   * Adds some new Cards after the last existing one in a given Collection\n   *\n   * @param collectionId the Id of the Collection\n   * @param cards the Seq[cards.nine.models.CardData] to add\n   * @return the Seq[cards.nine.models.Card] of the new cards\n   * @throws CardException if there was an error getting the existing cards or adding the new one\n   */\n  def addCards(collectionId: Int, cards: Seq[CardData]): TaskService[Seq[Card]]\n\n  /**\n   * Deletes a Card and updates the position of the other Cards in the Collection\n   *\n   * @param collectionId the Id of the Collection\n   * @param cardId the Id of the Card to delete\n   * @throws CardException if there was an error finding the card, getting the existing collection's cards, deleting the card or updating the rest of them\n   */\n  def deleteCard(collectionId: Int, cardId: Int): TaskService[Unit]\n\n  /**\n   * Delete all Cards in all collection by package name\n   *\n   * @param packageName package name that you want to remove\n   * @throws CardException if there was an error finding the card, getting the existing collection's cards, deleting the card or updating the rest of them\n   */\n  def deleteAllCardsByPackageName(packageName: String): TaskService[Unit]\n\n  /**\n   * Deletes several Card and updates the position of the other Cards in the Collection\n   *\n   * @param collectionId the Id of the Collection\n   * @param cardIds list of Ids of the Card to delete\n   * @throws CardException if there was an error finding the card, getting the existing collection's cards, deleting the card or updating the rest of them\n   */\n  def deleteCards(collectionId: Int, cardIds: Seq[Int]): TaskService[Unit]\n\n  /**\n   * Moves a Card to another position and updates the position of the other Cards in the Collection\n   *\n   * @param collectionId the Id of the Collection\n   * @param cardId the Id of the Card to delete\n   * @param newPosition the new position of the Card\n   * @throws CardException if there was an error finding the card, getting the existing cards or updating the position of all the cards\n   */\n  def reorderCard(collectionId: Int, cardId: Int, newPosition: Int): TaskService[Unit]\n\n  /**\n   * Edits a Card and allows to change its name\n   *\n   * @param collectionId the Id of the Collection\n   * @param cardId the Id of the Card to delete\n   * @param name the new name of the Card\n   * @return the [[cards.nine.models.Card]]\n   * @throws CardException if there was an error finding the card or updating it\n   */\n  def editCard(collectionId: Int, cardId: Int, name: String): TaskService[Card]\n\n  /**\n   * Convert cards not installed in card from a package name\n   *\n   * @param packageName package name of app that we want to convert\n   * @throws CardException if there was an error finding the card or updating it\n   */\n  def updateNoInstalledCardsInCollections(packageName: String)(\n      implicit contextSupport: ContextSupport): TaskService[Unit]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/collection/CollectionsExceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.collection\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class CollectionException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class CardException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class ContactException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsCollectionException {\n  implicit def collectionException = (t: Throwable) => CollectionException(t.getMessage, Option(t))\n\n  implicit def cardException = (t: Throwable) => CardException(t.getMessage, Option(t))\n\n  implicit def contactException = (t: Throwable) => ContactException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/collection/impl/CardsProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.collection.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.ops.SeqOps._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models\nimport cards.nine.models._\nimport cards.nine.models.types.{AppCardType, NoInstalledAppCardType}\nimport cards.nine.process.collection.{\n  CardException,\n  CollectionProcess,\n  ImplicitsCollectionException\n}\nimport cards.nine.services.persistence.ImplicitsPersistenceServiceExceptions\nimport monix.eval.Task\n\ntrait CardsProcessImpl\n    extends CollectionProcess\n    with NineCardsIntentConversions\n    with ImplicitsCollectionException {\n\n  self: CollectionProcessDependencies with ImplicitsPersistenceServiceExceptions =>\n\n  override def addCards(collectionId: Int, cards: Seq[CardData]) =\n    (for {\n      cardList <- persistenceServices.fetchCardsByCollection(collectionId)\n      size = cardList.size\n      cardsToAdd = cards.zipWithIndex map {\n        case (card, index) => card.copy(position = index + size)\n      }\n      addedCardList <- persistenceServices.addCards(Seq((collectionId, cardsToAdd)))\n    } yield addedCardList).resolve[CardException]\n\n  override def deleteCard(collectionId: Int, cardId: Int) =\n    (for {\n      _        <- persistenceServices.deleteCard(collectionId, cardId)\n      cardList <- persistenceServices.fetchCardsByCollection(collectionId)\n      _        <- updateCardList(reloadPositions(cardList))\n    } yield ()).resolve[CardException]\n\n  override def deleteAllCardsByPackageName(packageName: String) = {\n\n    def removeAllCards(collections: Seq[Collection]): TaskService[Unit] = {\n      val tasks = collections flatMap { collection =>\n        collection.cards.filter(_.packageName == Option(packageName)) map { card =>\n          deleteCard(collection.id, card.id).value\n        }\n      }\n      TaskService(Task.gatherUnordered(tasks).map(_ => Right((): Unit)))\n    }\n\n    (for {\n      collections <- persistenceServices.fetchCollections\n      _           <- removeAllCards(collections)\n    } yield ()).resolve[CardException]\n  }\n\n  override def deleteCards(collectionId: Int, cardIds: Seq[Int]) =\n    (for {\n      _        <- persistenceServices.deleteCards(collectionId, cardIds)\n      cardList <- persistenceServices.fetchCardsByCollection(collectionId)\n      _        <- updateCardList(reloadPositions(cardList))\n    } yield ()).resolve[CardException]\n\n  override def reorderCard(collectionId: Int, cardId: Int, newPosition: Int) = {\n\n    def reorderList(cardList: Seq[Card], oldPosition: Int): Seq[Card] = {\n      val (init, end) =\n        if (oldPosition > newPosition) (newPosition, oldPosition) else (oldPosition, newPosition)\n      cardList\n        .reorderRange(oldPosition, newPosition)\n        .zip(init to end)\n        .map({ case (card, index) => card.copy(position = index) })\n    }\n\n    def reorderAux(card: models.Card) =\n      if (card.position != newPosition)\n        for {\n          cardList <- persistenceServices.fetchCardsByCollection(collectionId)\n          _        <- updateCardList(reorderList(cardList, card.position))\n        } yield ()\n      else TaskService(Task(Right(Unit)))\n    (for {\n      card <- persistenceServices\n        .findCardById(cardId)\n        .resolveOption(s\"Can't find the card with id $cardId\")\n      _ <- reorderAux(card)\n    } yield ()).resolve[CardException]\n  }\n\n  override def editCard(collectionId: Int, cardId: Int, name: String) =\n    (for {\n      card <- persistenceServices\n        .findCardById(cardId)\n        .resolveOption(s\"Can't find the card with id $cardId\")\n      updatedCard = card.copy(term = name)\n      _ <- updateCard(updatedCard)\n    } yield updatedCard).resolve[CardException]\n\n  override def updateNoInstalledCardsInCollections(packageName: String)(\n      implicit contextSupport: ContextSupport) = {\n\n    def toCard(cards: Seq[Card], app: ApplicationData)(\n        implicit contextSupport: ContextSupport): Seq[Card] = {\n      val intent = toNineCardIntent(app)\n      cards map (_.copy(term = app.name, cardType = AppCardType, intent = intent))\n    }\n\n    (for {\n      app      <- appsServices.getApplication(packageName)\n      cardList <- persistenceServices.fetchCards\n      cardsNoInstalled = cardList filter (card =>\n                                            card.cardType == NoInstalledAppCardType && card.packageName\n                                              .contains(packageName))\n      cards = toCard(cardsNoInstalled, app)\n      _ <- updateCardList(cards)\n    } yield ()).resolve[CardException]\n\n  }\n\n  private[this] def reloadPositions(cardList: Seq[Card]) = cardList.zipWithIndex flatMap {\n    case (card, position) if card.position != position => Option(card.copy(position = position))\n    case _                                             => None\n  }\n\n  private[this] def updateCard(card: Card) =\n    (for {\n      _ <- persistenceServices.updateCard(card)\n    } yield ()).resolve[CardException]\n\n  private[this] def updateCardList(cardList: Seq[Card]) =\n    (for {\n      _ <- persistenceServices.updateCards(cardList)\n    } yield ()).resolve[CardException]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/collection/impl/CollectionProcessDependencies.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.collection.impl\n\nimport cards.nine.models.CollectionProcessConfig\nimport cards.nine.services.api.ApiServices\nimport cards.nine.services.apps.AppsServices\nimport cards.nine.services.awareness.AwarenessServices\nimport cards.nine.services.contacts.ContactsServices\nimport cards.nine.services.persistence.PersistenceServices\nimport cards.nine.services.widgets.WidgetsServices\n\ntrait CollectionProcessDependencies {\n\n  val collectionProcessConfig: CollectionProcessConfig\n  val persistenceServices: PersistenceServices\n  val contactsServices: ContactsServices\n  val appsServices: AppsServices\n  val apiServices: ApiServices\n  val awarenessServices: AwarenessServices\n  val widgetsServices: WidgetsServices\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/collection/impl/CollectionProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.collection.impl\n\nimport cards.nine.models.CollectionProcessConfig\nimport cards.nine.process.collection._\nimport cards.nine.services.api.ApiServices\nimport cards.nine.services.apps.AppsServices\nimport cards.nine.services.awareness.AwarenessServices\nimport cards.nine.services.contacts.ContactsServices\nimport cards.nine.services.persistence.{ImplicitsPersistenceServiceExceptions, PersistenceServices}\nimport cards.nine.services.widgets.WidgetsServices\n\nclass CollectionProcessImpl(\n    val collectionProcessConfig: CollectionProcessConfig,\n    val persistenceServices: PersistenceServices,\n    val contactsServices: ContactsServices,\n    val appsServices: AppsServices,\n    val apiServices: ApiServices,\n    val awarenessServices: AwarenessServices,\n    val widgetsServices: WidgetsServices)\n    extends CollectionProcess\n    with CollectionProcessDependencies\n    with CollectionsProcessImpl\n    with CardsProcessImpl\n    with ImplicitsPersistenceServiceExceptions\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/collection/impl/CollectionsProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.collection.impl\n\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.ops.SeqOps._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.Application.ApplicationDataOps\nimport cards.nine.models.types.NineCardsCategory._\nimport cards.nine.models.types._\nimport cards.nine.models.{RankApps, _}\nimport cards.nine.process.collection._\nimport cards.nine.process.utils.ApiUtils\nimport cards.nine.services.persistence.ImplicitsPersistenceServiceExceptions\nimport cats.syntax.either._\nimport monix.eval.Task\n\nimport scala.annotation.tailrec\n\ntrait CollectionsProcessImpl\n    extends CollectionProcess\n    with NineCardsIntentConversions\n    with ImplicitsCollectionException {\n\n  self: CollectionProcessDependencies with ImplicitsPersistenceServiceExceptions =>\n\n  val minAppsGenerateCollections = 1\n\n  val apiUtils = new ApiUtils(persistenceServices)\n\n  def createCollectionsFromCollectionData(collectionDataSeq: Seq[CollectionData])(\n      implicit context: ContextSupport) = {\n\n    def adaptCardToApp(card: CardData, apps: Seq[ApplicationData]) = {\n      val packageName = card.intent.extractPackageName()\n      apps find (app => packageName.contains(app.packageName)) match {\n        case Some(app) if card.intent.extractClassName().contains(app.className) =>\n          card.copy(cardType = AppCardType)\n        case Some(app) => card.copy(intent = toNineCardIntent(app), cardType = AppCardType)\n        case None      => card.copy(cardType = NoInstalledAppCardType)\n      }\n    }\n\n    def adaptCardsToAppsInstalled(\n        collections: Seq[CollectionData],\n        apps: Seq[ApplicationData]): Seq[CollectionData] = {\n      collections map { collection =>\n        val cardsWithPath = collection.cards map {\n          case card if card.cardType == AppCardType || card.cardType == RecommendedAppCardType =>\n            adaptCardToApp(card, apps)\n          case card => card\n        }\n        collection.copy(cards = cardsWithPath)\n      }\n    }\n\n    (for {\n      apps <- appsServices.getInstalledApplications\n      collectionsRequest = adaptCardsToAppsInstalled(collectionDataSeq, apps)\n      collections <- persistenceServices.addCollections(collectionsRequest)\n    } yield collections).resolve[CollectionException]\n  }\n\n  def generatePrivateCollections(apps: Seq[ApplicationData])(implicit context: ContextSupport) =\n    TaskService {\n      CatchAll[CollectionException] {\n\n        @tailrec\n        def createPrivateCollections(\n            items: Seq[ApplicationData],\n            categories: Seq[NineCardsCategory],\n            acc: Seq[CollectionData]): Seq[CollectionData] = categories match {\n          case Nil => acc\n          case h :: t =>\n            val insert = createPrivateCollection(items, h, acc.length)\n            val a      = if (insert.cards.nonEmpty) acc :+ insert else acc\n            createPrivateCollections(items, t, a)\n        }\n\n        def createPrivateCollection(\n            items: Seq[ApplicationData],\n            category: NineCardsCategory,\n            position: Int): CollectionData = {\n          // TODO We should sort the application using an endpoint in the new sever\n          val appsByCategory = items.filter(_.category.toAppCategory == category)\n          CollectionData(\n            position = position,\n            name = collectionProcessConfig.namesCategories\n              .getOrElse(category, category.getStringResource),\n            collectionType = AppsCollectionType,\n            icon = category.getStringResource,\n            themedColorIndex = 0,\n            appsCategory = Some(category),\n            cards = appsByCategory map (_.toCardData),\n            moment = None,\n            originalSharedCollectionId = None,\n            sharedCollectionId = None,\n            sharedCollectionSubscribed = false,\n            publicCollectionStatus = NotPublished)\n        }\n\n        createPrivateCollections(apps, appsCategories, Seq.empty)\n      }\n    }\n\n  def getCollections = persistenceServices.fetchCollections.resolve[CollectionException]\n\n  def getCollectionById(collectionId: Int) =\n    persistenceServices.findCollectionById(collectionId).resolve[CollectionException]\n\n  def getCollectionByCategory(category: NineCardsCategory) =\n    persistenceServices.findCollectionByCategory(category.name).resolve[CollectionException]\n\n  def getCollectionBySharedCollectionId(sharedCollectionId: String) =\n    persistenceServices\n      .fetchCollectionBySharedCollectionId(sharedCollectionId)\n      .resolve[CollectionException]\n\n  def addCollection(collection: CollectionData) =\n    (for {\n      collectionList <- persistenceServices.fetchCollections\n      collection <- persistenceServices.addCollection(\n        collection.copy(position = collectionList.size))\n    } yield collection).resolve[CollectionException]\n\n  def deleteCollection(collectionId: Int) = {\n\n    def moveCollectionList(collectionList: Seq[Collection], position: Int) =\n      collectionList flatMap {\n        case collection if collection.position > position =>\n          Option(collection.copy(position = collection.position - 1))\n        case _ => None\n      }\n\n    (for {\n      collection     <- findCollectionById(collectionId)\n      _              <- persistenceServices.deleteCollection(collection)\n      _              <- persistenceServices.deleteCardsByCollection(collectionId)\n      collectionList <- getCollections\n      _              <- updateCollectionList(moveCollectionList(collectionList, collection.position))\n    } yield ()).resolve[CollectionException]\n  }\n\n  def cleanCollections() =\n    (for {\n      _ <- persistenceServices.deleteAllCollections()\n      _ <- persistenceServices.deleteAllCards()\n    } yield ()).resolve[CollectionException]\n\n  def reorderCollection(position: Int, newPosition: Int) =\n    (for {\n      collectionList <- getCollections\n      (from, to) = if (position > newPosition) (newPosition, position) else (position, newPosition)\n      updatedCollections = collectionList.reorderRange(position, newPosition).zip(from to to) map {\n        case (c, index) => c.copy(position = index)\n      }\n      _ <- updateCollectionList(updatedCollections)\n    } yield ()).resolve[CollectionException]\n\n  def editCollection(collectionId: Int, collectionData: CollectionData) =\n    editCollectionWith(collectionId) { collection =>\n      collection.copy(\n        name = collectionData.name,\n        icon = collectionData.icon,\n        themedColorIndex = collectionData.themedColorIndex,\n        appsCategory = collectionData.appsCategory)\n    }\n\n  def updateSharedCollection(collectionId: Int, sharedCollectionId: String) =\n    editCollectionWith(collectionId)(\n      _.copy(sharedCollectionId = Some(sharedCollectionId), sharedCollectionSubscribed = false))\n\n  def addPackages(collectionId: Int, packages: Seq[String])(implicit context: ContextSupport) = {\n\n    def fetchPackagesNotAddedToCollection(): TaskService[(Int, Seq[String])] =\n      for {\n        cards <- persistenceServices.fetchCardsByCollection(collectionId)\n        actualCollectionSize = cards.size\n        notAdded = packages.filterNot(packageName =>\n          cards.exists(_.packageName.contains(packageName)))\n      } yield (cards.size, notAdded)\n\n    def fetchInstalledPackages(packages: Seq[String]): TaskService[Seq[Application]] =\n      if (packages.isEmpty) {\n        TaskService(Task(Either.right(Seq.empty)))\n      } else {\n        persistenceServices.fetchAppByPackages(packages)\n      }\n\n    def categorizeNotInstalledPackages(\n        installedApps: Seq[Application],\n        notAdded: Seq[String]): TaskService[Seq[CategorizedDetailPackage]] = {\n      val notInstalledApps =\n        notAdded.filterNot(packageName => installedApps.exists(_.packageName == packageName))\n      if (notInstalledApps.isEmpty) {\n        TaskService(Task(Either.right(Seq.empty)))\n      } else {\n        for {\n          requestConfig <- apiUtils.getRequestConfig\n          packages      <- apiServices.googlePlayPackagesDetail(notInstalledApps)(requestConfig)\n        } yield packages\n      }\n    }\n\n    def addCards(\n        actualCollectionSize: Int,\n        installedApps: Seq[Application],\n        categorizedPackages: Seq[CategorizedDetailPackage]): TaskService[Unit] = {\n\n      def toCardDataFromInstalled(app: Application, position: Int) =\n        CardData(\n          position = position,\n          term = app.name,\n          packageName = Option(app.packageName),\n          cardType = AppCardType,\n          intent = toNineCardIntent(app),\n          imagePath = None)\n\n      def toCardDataFromNotInstalled(\n          categorizedPackage: CategorizedDetailPackage,\n          cardType: CardType,\n          position: Int) =\n        CardData(\n          term = categorizedPackage.title,\n          position = position,\n          packageName = Option(categorizedPackage.packageName),\n          cardType = cardType,\n          intent = packageToNineCardIntent(categorizedPackage.packageName),\n          imagePath = None)\n\n      if (installedApps.isEmpty && categorizedPackages.isEmpty) {\n        TaskService(Task(Either.right((): Unit)))\n      } else {\n        val installedRequests = installedApps map (app =>\n                                                     (collectionId,\n                                                      toCardDataFromInstalled(app, 0)))\n        val notInstalledRequests = categorizedPackages map { detailPackage =>\n          (collectionId, toCardDataFromNotInstalled(detailPackage, NoInstalledAppCardType, 0))\n        }\n        val cardsToAdd = (installedRequests ++ notInstalledRequests).zipWithIndex.map {\n          case ((_, card), index) => card.copy(position = actualCollectionSize + index)\n        }\n\n        persistenceServices.addCards(Seq((collectionId, cardsToAdd))).map(_ => ())\n      }\n    }\n\n    (for {\n      _     <- findCollectionById(collectionId)\n      tuple <- fetchPackagesNotAddedToCollection()\n      (actualCollectionSize, notAdded) = tuple\n      installedApps   <- fetchInstalledPackages(notAdded)\n      fetchedPackages <- categorizeNotInstalledPackages(installedApps, notAdded)\n      _               <- addCards(actualCollectionSize, installedApps, fetchedPackages)\n    } yield ()).resolve[CollectionException]\n  }\n\n  def rankApps()(implicit context: ContextSupport) = {\n\n    def mapValues(seq: Seq[(NineCardsCategory, String)]): Seq[(NineCardsCategory, Seq[String])] =\n      seq.groupBy(_._1).mapValues(_.map(_._2)).toSeq\n\n    def generatePackagesByCategory(packagesByCategory: (NineCardsCategory, Seq[String])) = {\n      val (category, packages) = packagesByCategory\n      PackagesByCategory(category = category, packages = packages)\n    }\n\n    def generatePackagesByCategoryFromRankApps(item: RankApps) =\n      PackagesByCategory(category = item.category, packages = item.packages)\n\n    def getPackagesByCategory: TaskService[Seq[(NineCardsCategory, Seq[String])]] =\n      for {\n        appList <- persistenceServices.fetchApps(OrderByCategory)\n      } yield\n        mapValues(\n          appList filterNot (_.category == Misc) map (app => (app.category, app.packageName)))\n\n    (for {\n      requestConfig      <- apiUtils.getRequestConfig\n      packagesByCategory <- getPackagesByCategory\n      location           <- awarenessServices.getLocation.map(Option(_)).resolveLeftTo(None)\n      result <- apiServices.rankApps(\n        packagesByCategory map generatePackagesByCategory,\n        location flatMap (_.countryCode))(requestConfig)\n    } yield result map generatePackagesByCategoryFromRankApps).resolve[CollectionException]\n  }\n\n  def rankAppsByMoment(limit: Int)(implicit context: ContextSupport) = {\n\n    def toPackagesByMoment(rankAppsByMoment: Seq[RankAppsByMoment]) =\n      rankAppsByMoment map (ra => PackagesByMoment(ra.moment, ra.packages))\n\n    (for {\n      requestConfig <- apiUtils.getRequestConfig\n      appList       <- persistenceServices.fetchApps(OrderByName)\n      momentList    <- persistenceServices.fetchMoments\n      location      <- awarenessServices.getLocation.map(Option(_)).resolveLeftTo(None)\n      result <- apiServices.rankAppsByMoment(\n        appList map (_.packageName),\n        momentList map (_.momentType.name),\n        location flatMap (_.countryCode),\n        limit = limit)(requestConfig)\n    } yield toPackagesByMoment(result)).resolve[CollectionException]\n  }\n\n  def rankWidgetsByMoment(limit: Int, moments: Seq[NineCardsMoment])(\n      implicit context: ContextSupport) = {\n\n    def toAppWidget(rankWidget: RankWidget, appWidgets: Seq[AppWidget]) =\n      appWidgets.find(appWidget =>\n        appWidget.packageName == rankWidget.packageName && appWidget.className == rankWidget.className)\n\n    def toWidgetsByMoment(\n        rankWidgetsByMoment: Seq[RankWidgetsByMoment],\n        appWidgets: Seq[AppWidget]) =\n      rankWidgetsByMoment map { rankWidget =>\n        WidgetsByMoment(\n          moment = rankWidget.moment,\n          widgets = rankWidget.widgets flatMap (widget => toAppWidget(widget, appWidgets)))\n      }\n\n    (for {\n      requestConfig <- apiUtils.getRequestConfig\n      appList       <- persistenceServices.fetchApps(OrderByName)\n      location      <- awarenessServices.getLocation.map(Option(_)).resolveLeftTo(None)\n      appWidgets    <- widgetsServices.getWidgets\n      result <- apiServices.rankWidgetsByMoment(\n        appList map (_.packageName),\n        moments map (_.name),\n        location flatMap (_.countryCode),\n        limit = limit)(requestConfig)\n    } yield toWidgetsByMoment(result, appWidgets)).resolve[CollectionException]\n  }\n\n  private[this] def editCollectionWith(collectionId: Int)(f: (Collection) => Collection) =\n    (for {\n      collection <- findCollectionById(collectionId)\n      updatedCollection = f(collection)\n      _ <- persistenceServices.updateCollection(updatedCollection)\n    } yield updatedCollection).resolve[CollectionException]\n\n  private[this] def findCollectionById(collectionId: Int) =\n    persistenceServices\n      .findCollectionById(collectionId)\n      .resolveOption(s\"Can't find the collection with id $collectionId\")\n\n  private[this] def updateCollectionList(collectionList: Seq[Collection]) =\n    persistenceServices.updateCollections(collectionList)\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/device/DeviceExceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class ResetException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class AppException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class CreateBitmapException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class ShortcutException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class ContactException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class ContactPermissionException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class WidgetException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class CallException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class CallPermissionException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class DeviceException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class DockAppException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsDeviceException {\n  implicit def resetException = (t: Throwable) => ResetException(t.getMessage, Option(t))\n\n  implicit def appException = (t: Throwable) => AppException(t.getMessage, Option(t))\n\n  implicit def createBitmapException =\n    (t: Throwable) => CreateBitmapException(t.getMessage, Option(t))\n\n  implicit def shortcutException = (t: Throwable) => ShortcutException(t.getMessage, Option(t))\n\n  implicit def contactException = (t: Throwable) => ContactException(t.getMessage, Option(t))\n\n  implicit def widgetException = (t: Throwable) => WidgetException(t.getMessage, Option(t))\n\n  implicit def callException = (t: Throwable) => CallException(t.getMessage, Option(t))\n\n  implicit def dockAppException = (t: Throwable) => DockAppException(t.getMessage, Option(t))\n\n  implicit def deviceException = (t: Throwable) => DeviceException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/device/DeviceProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device\n\nimport android.content.Intent.ShortcutIconResource\nimport android.graphics.Bitmap\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models._\nimport cards.nine.models.types.{AllContacts, ContactsFilter, DockType, GetAppOrder}\n\ntrait DeviceProcess {\n\n  /**\n   * Delete all apps, cards, collections and dockApps from the repository\n   *\n   * @throws ResetException if exist some problem deleting the apps, cards, collections and dockApps\n   */\n  def resetSavedItems(): TaskService[Unit]\n\n  /**\n   * Get the saved apps from the database\n   *\n   * @param orderBy indicates the order to fetch the apps\n   * @return the Seq[cards.nine.models.ApplicationData]\n   * @throws AppException if exist some problem getting the apps\n   */\n  def getSavedApps(orderBy: GetAppOrder)(\n      implicit context: ContextSupport): TaskService[Seq[ApplicationData]]\n\n  /**\n   * Get iterable of saved apps from the database\n   *\n   * @param orderBy indicates the order to fetch the apps\n   * @return the cards.nine.process.device.models.IterableApps contains\n   *         information about the app\n   * @throws AppException if exist some problem getting the apps\n   */\n  def getIterableApps(orderBy: GetAppOrder)(\n      implicit context: ContextSupport): TaskService[IterableApplicationData]\n\n  /**\n   * Get iterable by category of saved apps from the database\n   *\n   * @param category indicates the category\n   * @return the cards.nine.process.device.models.IterableApps contains\n   *         information about the app\n   * @throws AppException if exist some problem getting the apps\n   */\n  def getIterableAppsByCategory(category: String)(\n      implicit context: ContextSupport): TaskService[IterableApplicationData]\n\n  /**\n   * Returns a sequence that contains all the distinct apps' first letter and the number of apps whose name\n   * starts with this letter\n   *\n   * @param orderBy indicates the order to fetch the apps\n   * @return the Seq[cards.nine.models.TermCounter] contains\n   *         information about the times is repeated an apps\n   * @throws AppException if exist some problem getting the contacts\n   */\n  def getTermCountersForApps(orderBy: GetAppOrder)(\n      implicit context: ContextSupport): TaskService[Seq[TermCounter]]\n\n  /**\n   * Get the iterable apps by keyword.\n   *\n   * @param keyword the filter for the query\n   * @param orderBy indicates the order to fetch the apps\n   * @return the cards.nine.process.device.models.IterableApps contains\n   *         information about the app\n   * @throws AppException if exist some problem getting the contacts\n   */\n  def getIterableAppsByKeyWord(keyword: String, orderBy: GetAppOrder)(\n      implicit context: ContextSupport): TaskService[IterableApplicationData]\n\n  /**\n   * Get the available applications that contain shortcuts creating Intents and Drawables necessaries for UI actions\n   *\n   * @return the Seq[cards.nine.models.Shortcut] contains\n   *         information about shortcut with the Intents and Drawables for UI actions\n   * @throws ShortcutException if exist some problem getting the shortcuts in the cell phone\n   */\n  def getAvailableShortcuts(implicit context: ContextSupport): TaskService[Seq[Shortcut]]\n\n  /**\n   * Save shortcut icon from bitmap\n   *\n   * @param bitmap the image\n   * @param iconResize optional parameter that indicates some resizing arguments\n   * @return the String contains the path where the icon was stored\n   * @throws ShortcutException if exist some problem storing icon\n   */\n  def saveShortcutIcon(bitmap: Bitmap, iconResize: Option[IconResize] = None)(\n      implicit context: ContextSupport): TaskService[String]\n\n  /**\n   * Extract a bitmap from a ShortcutIconResource\n   *\n   * @param resource the ShortcutIconResource\n   * @return the decoded bitmap\n   * @throws ShortcutException if exist some problem decoding the icon\n   */\n  def decodeShortcutIcon(resource: ShortcutIconResource)(\n      implicit context: ContextSupport): TaskService[Bitmap]\n\n  /**\n   * Get the favorite contacts if they exist and fill all their data\n   *\n   * @return the Seq[cards.nine.models.Contact] contains\n   *         information about the contact including its ContactInfo (if it exists)\n   * @throws ContactPermissionException if the permission to read contacts hasn't been granted\n   * @throws ContactException if exist some problem getting the favorite contacts\n   */\n  def getFavoriteContacts(implicit context: ContextSupport): TaskService[Seq[Contact]]\n\n  /**\n   * Get the contacts by filter selected sorted without data. The filters are: all contacts, favorite contacts\n   * and contacts with phone number\n   *\n   * @param filter specify the filter in the query\n   * @return the Seq[cards.nine.models.Contact] contains\n   *         information about the contact\n   * @throws ContactPermissionException if the permission to read contacts hasn't been granted\n   * @throws ContactException if exist some problem getting the contacts\n   */\n  def getContacts(filter: ContactsFilter = AllContacts)(\n      implicit context: ContextSupport): TaskService[Seq[Contact]]\n\n  /**\n   * Returns a sequence that contains all the distinct contacts' first letter and the number of contacts whose name\n   * starts with this letter\n   *\n   * @param filter specify the filter in the query\n   * @return the Seq[cards.nine.models.TermCounter]\n   * @throws ContactPermissionException if the permission to read contacts hasn't been granted\n   * @throws ContactException if exist some problem getting the contacts\n   */\n  def getTermCountersForContacts(filter: ContactsFilter = AllContacts)(\n      implicit context: ContextSupport): TaskService[Seq[TermCounter]]\n\n  /**\n   * Get the iterable contacts by filter selected sorted without data. The filters are: all contacts, favorite contacts\n   * and contacts with phone number\n   *\n   * @param filter specify the filter in the query\n   * @return the cards.nine.process.device.models.IterableContacts contains\n   *         information about the contact\n   * @throws ContactPermissionException if the permission to read contacts hasn't been granted\n   * @throws ContactException if exist some problem getting the contacts\n   */\n  def getIterableContacts(filter: ContactsFilter = AllContacts)(\n      implicit context: ContextSupport): TaskService[IterableContacts]\n\n  /**\n   * Get the contact and fill all their data\n   *\n   * @param lookupKey the contact lookup key\n   * @return the cards.nine.models.Contact contains\n   *         information about the contact\n   * @throws ContactPermissionException if the permission to read contacts hasn't been granted\n   * @throws ContactException if exist some problem getting the contacts\n   */\n  def getContact(lookupKey: String)(implicit context: ContextSupport): TaskService[Contact]\n\n  /**\n   * Get the iterable contacts by keyword.\n   *\n   * @param keyword the filter for the query\n   * @return the cards.nine.process.device.models.IterableContacts contains\n   *         information about the contact\n   * @throws ContactPermissionException if the permission to read contacts hasn't been granted\n   * @throws ContactException if exist some problem getting the contacts\n   */\n  def getIterableContactsByKeyWord(keyword: String)(\n      implicit context: ContextSupport): TaskService[IterableContacts]\n\n  /**\n   * Fetches the installed apps in the device and synchronizes them with the database, categorizing the apps not\n   * stored in the database\n   *\n   * @throws AppException if exist some problem\n   */\n  def synchronizeInstalledApps(implicit context: ContextSupport): TaskService[Unit]\n\n  /**\n   * Get an installed app and store it in the repository\n   *\n   * @param packageName the packageName of the app to save\n   * @throws AppException if exist some problem getting the app or storing it\n   */\n  def saveApp(packageName: String)(implicit context: ContextSupport): TaskService[ApplicationData]\n\n  /**\n   * Delete an app from the repository\n   *\n   * @param packageName the packageName of the app to delete\n   * @throws AppException if exist some problem deleting the app\n   */\n  def deleteApp(packageName: String)(implicit context: ContextSupport): TaskService[Unit]\n\n  /**\n   * update app by packageName\n   *\n   * @param packageName the packageName of the app to update\n   * @throws AppException if exist some problem getting the app or updating it\n   */\n  def updateApp(packageName: String)(implicit context: ContextSupport): TaskService[Unit]\n\n  /**\n   * Get the widgets available on the phone\n   *\n   * @return the Seq[cards.nine.models.AppsWithWidgets]\n   * @throws WidgetException if exist some problem getting the widgets\n   */\n  def getWidgets(implicit context: ContextSupport): TaskService[Seq[AppsWithWidgets]]\n\n  /**\n   * Get the last calls available on the phone\n   *\n   * @return the Seq[cards.nine.models.LastCallsContact]\n   * @throws CallPermissionException if the permission to read calls hasn't been granted\n   * @throws CallException if exist some problem getting the last calls\n   */\n  def getLastCalls(implicit context: ContextSupport): TaskService[Seq[LastCallsContact]]\n\n  /**\n   * Generate the docks apps available for user\n   *\n   * @param size of the dock apps needed\n   * @return the Seq[cards.nine.models.DockApp]\n   * @throws DockAppException if exist some problem getting the app or storing it\n   */\n  def generateDockApps(size: Int)(implicit context: ContextSupport): TaskService[Seq[DockApp]]\n\n  /**\n   * Create or update a dock app\n   *\n   * @param name name of dock app\n   * @param dockType dock type\n   * @param intent action\n   * @param imagePath image\n   * @param position new position\n   * @throws DockAppException if exist some problem getting the app or storing it\n   */\n  def createOrUpdateDockApp(\n      name: String,\n      dockType: DockType,\n      intent: NineCardsIntent,\n      imagePath: String,\n      position: Int): TaskService[Unit]\n\n  /**\n   * Creates DockApps from some already formed and given DockApps\n   *\n   * @param items the Seq[cards.nine.models.DockApp] of DockApps\n   * @return the Seq[cards.nine.models.DockAppData]\n   * @throws DockAppException if there was an error creating the moments' collections\n   */\n  def saveDockApps(items: Seq[DockAppData]): TaskService[Seq[DockApp]]\n\n  /**\n   * Get the docks apps available for user\n   *\n   * @return the Seq[cards.nine.models.DockApp]\n   * @throws DockAppException if exist some problem getting the app or storing it\n   */\n  def getDockApps: TaskService[Seq[DockApp]]\n\n  /**\n   * Delete all dock apps in database\n   *\n   * @throws DockAppException if exist some problem getting the app or storing it\n   */\n  def deleteAllDockApps(): TaskService[Unit]\n\n  /**\n   * Delete dock app by position\n   *\n   * @param position position that you want to remove\n   * @throws DockAppException if exist some problem getting the app or storing it\n   */\n  def deleteDockAppByPosition(position: Int): TaskService[Unit]\n\n  /**\n   * Get all configured networks sorted by name\n   *\n   * @return Seq[String] that contains all SSIDs\n   * @throws DeviceException if exist some problem getting devices\n   */\n  def getConfiguredNetworks(implicit context: ContextSupport): TaskService[Seq[String]]\n\n  /**\n   * Get all paired bluetooth devices sorted by name\n   *\n   * @return Seq[String] that contains all paired devices\n   * @throws DeviceException if exist some problem getting devices\n   */\n  def getPairedBluetoothDevices(implicit context: ContextSupport): TaskService[Seq[String]]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/device/impl/AppsDeviceProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.Application.ApplicationDataOps\nimport cards.nine.models.types.{Misc, _}\nimport cards.nine.models.{Application, ApplicationData}\nimport cards.nine.process.device._\nimport cards.nine.process.utils.ApiUtils\nimport cards.nine.repository.model.{App => RepositoryApp}\nimport cards.nine.services.image._\nimport cards.nine.services.persistence.ImplicitsPersistenceServiceExceptions\nimport cards.nine.services.persistence.conversions.AppConversions\n\ntrait AppsDeviceProcessImpl extends DeviceProcess with AppConversions {\n\n  self: DeviceProcessDependencies\n    with ImplicitsDeviceException\n    with ImplicitsImageExceptions\n    with ImplicitsPersistenceServiceExceptions =>\n\n  val apiUtils = new ApiUtils(persistenceServices)\n\n  def getSavedApps(orderBy: GetAppOrder)(implicit context: ContextSupport) =\n    (for {\n      apps <- persistenceServices.fetchApps(toFetchAppOrder(orderBy), orderBy.ascending)\n    } yield apps map (_.toData)).resolve[AppException]\n\n  def getIterableApps(orderBy: GetAppOrder)(implicit context: ContextSupport) =\n    (for {\n      iter <- persistenceServices.fetchIterableApps(toFetchAppOrder(orderBy), orderBy.ascending)\n    } yield iter).resolve[AppException]\n\n  def getIterableAppsByCategory(category: String)(implicit context: ContextSupport) =\n    (for {\n      iter <- persistenceServices.fetchIterableAppsByCategory(\n        category,\n        OrderByName,\n        ascending = true)\n    } yield iter).resolve[AppException]\n\n  def getTermCountersForApps(orderBy: GetAppOrder)(implicit context: ContextSupport) =\n    (for {\n      counters <- orderBy match {\n        case GetByName     => persistenceServices.fetchAlphabeticalAppsCounter\n        case GetByCategory => persistenceServices.fetchCategorizedAppsCounter\n        case _             => persistenceServices.fetchInstallationDateAppsCounter\n      }\n    } yield counters).resolve[AppException]\n\n  def getIterableAppsByKeyWord(keyword: String, orderBy: GetAppOrder)(\n      implicit context: ContextSupport) =\n    (for {\n      iter <- persistenceServices.fetchIterableAppsByKeyword(\n        keyword,\n        toFetchAppOrder(orderBy),\n        orderBy.ascending)\n    } yield iter).resolve[AppException]\n\n  def synchronizeInstalledApps(implicit context: ContextSupport) = {\n\n    def deleteAndFilter(\n        existingApps: Seq[Application],\n        idsToRemove: Seq[Int]): TaskService[Seq[Application]] =\n      if (idsToRemove.nonEmpty) {\n        for {\n          _ <- persistenceServices.deleteAppsByIds(idsToRemove)\n          filteredApps <- TaskService.right(\n            existingApps.filterNot(app => idsToRemove.contains(app.id)))\n        } yield filteredApps\n      } else TaskService.right(existingApps)\n\n    def fixDuplicatedPackages: TaskService[Seq[Application]] = {\n      for {\n        allApps <- persistenceServices.fetchApps(OrderByInstallDate, ascending = false)\n        (miscAps, categorizedApps) = allApps.partition(_.category == Misc)\n        duplicatedIds = categorizedApps\n          .groupBy(app => s\"${app.packageName}:${app.className}\")\n          .flatMap {\n            case (packageName, seq) => seq.tail.map(_.id)\n          }\n          .toSeq\n        miscIds = miscAps.map(_.id)\n        filteredApps <- deleteAndFilter(\n          existingApps = allApps,\n          idsToRemove = (duplicatedIds ++ miscIds).distinct)\n      } yield filteredApps\n    }\n\n    def categorizeAndSaveNewApps(filteredApps: Seq[ApplicationData]): TaskService[Unit] =\n      if (filteredApps.nonEmpty) {\n        for {\n          requestConfig <- apiUtils.getRequestConfig\n          categorizedPackages <- apiServices\n            .googlePlayPackages(filteredApps map (_.packageName))(requestConfig)\n            .resolveLeftTo(Seq.empty)\n          apps = filteredApps map { app =>\n            val category = categorizedPackages find (_.packageName == app.packageName) flatMap (_.category) getOrElse Misc\n            app.copy(category = category)\n          }\n          _ <- persistenceServices.addApps(apps)\n        } yield ()\n      } else TaskService.empty\n\n    (for {\n      installedApps <- appsServices.getInstalledApplications\n      dbApps        <- fixDuplicatedPackages\n      filteredApps <- TaskService.right(\n        installedApps.filterNot(app => dbApps.exists(_.packageName == app.packageName)))\n      _ <- categorizeAndSaveNewApps(filteredApps)\n    } yield ()).resolve[AppException]\n  }\n\n  def saveApp(packageName: String)(implicit context: ContextSupport) =\n    (for {\n      application      <- appsServices.getApplication(packageName)\n      appCategory      <- getAppCategory(packageName)\n      applicationAdded <- persistenceServices.addApp(application.copy(category = appCategory))\n    } yield applicationAdded.toData).resolve[AppException]\n\n  def deleteApp(packageName: String)(implicit context: ContextSupport) =\n    (for {\n      _ <- persistenceServices.deleteAppByPackage(packageName)\n    } yield ()).resolve[AppException]\n\n  def updateApp(packageName: String)(implicit context: ContextSupport) =\n    (for {\n      app <- appsServices.getApplication(packageName)\n      appPersistence <- persistenceServices\n        .findAppByPackage(packageName)\n        .resolveOption(s\"Can't find the application with package name $packageName\")\n      appCategory <- getAppCategory(packageName)\n      _           <- persistenceServices.updateApp(app.copy(category = appCategory).toApp(appPersistence.id))\n    } yield ()).resolve[AppException]\n\n  private[this] def toFetchAppOrder(orderBy: GetAppOrder): FetchAppOrder = orderBy match {\n    case GetByName(_)        => OrderByName\n    case GetByInstallDate(_) => OrderByInstallDate\n    case GetByCategory(_)    => OrderByCategory\n  }\n\n  private[this] def getAppCategory(packageName: String)(implicit context: ContextSupport) =\n    for {\n      requestConfig <- apiUtils.getRequestConfig\n      appCategory <- apiServices\n        .googlePlayPackage(packageName)(requestConfig)\n        .map(_.category)\n        .resolveLeftTo(None)\n    } yield appCategory getOrElse Misc\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/device/impl/ContactsDeviceProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{IterableContacts, TermCounter}\nimport cards.nine.models.types.{\n  AllContacts,\n  ContactsFilter,\n  ContactsWithPhoneNumber,\n  FavoriteContacts\n}\nimport cards.nine.process.device._\nimport cards.nine.services.contacts.ContactsServicePermissionException\nimport monix.eval.Task\n\ntrait ContactsDeviceProcessImpl extends DeviceProcess {\n\n  self: DeviceProcessDependencies with ImplicitsDeviceException =>\n\n  val emptyContactCounterService: TaskService[Seq[TermCounter]] =\n    TaskService(Task(Right(Seq.empty)))\n\n  def mapServicesException[E >: NineCardException]: (NineCardException => E) = {\n    case e: ContactsServicePermissionException => ContactPermissionException(e.message, Some(e))\n    case e                                     => ContactException(e.getMessage, Option(e))\n  }\n\n  def getFavoriteContacts(implicit context: ContextSupport) =\n    (for {\n      favoriteContacts       <- contactsServices.getFavoriteContacts\n      filledFavoriteContacts <- contactsServices.populateContactInfo(favoriteContacts)\n    } yield filledFavoriteContacts).leftMap(mapServicesException)\n\n  def getContacts(filter: ContactsFilter = AllContacts)(implicit context: ContextSupport) =\n    (for {\n      contacts <- filter match {\n        case AllContacts             => contactsServices.getContacts\n        case FavoriteContacts        => contactsServices.getFavoriteContacts\n        case ContactsWithPhoneNumber => contactsServices.getContactsWithPhone\n      }\n    } yield contacts).leftMap(mapServicesException)\n\n  def getTermCountersForContacts(filter: ContactsFilter = AllContacts)(\n      implicit context: ContextSupport) =\n    (for {\n      counters <- filter match {\n        case AllContacts             => contactsServices.getAlphabeticalCounterContacts\n        case FavoriteContacts        => emptyContactCounterService\n        case ContactsWithPhoneNumber => emptyContactCounterService\n      }\n    } yield counters).leftMap(mapServicesException)\n\n  def getIterableContacts(filter: ContactsFilter = AllContacts)(implicit context: ContextSupport) =\n    (for {\n      iter <- filter match {\n        case AllContacts             => contactsServices.getIterableContacts\n        case FavoriteContacts        => contactsServices.getIterableFavoriteContacts\n        case ContactsWithPhoneNumber => contactsServices.getIterableContactsWithPhone\n      }\n    } yield new IterableContacts(iter)).leftMap(mapServicesException)\n\n  def getIterableContactsByKeyWord(keyword: String)(implicit context: ContextSupport) =\n    (for {\n      iter <- contactsServices.getIterableContactsByKeyword(keyword)\n    } yield new IterableContacts(iter)).resolve[ContactException]\n\n  def getContact(lookupKey: String)(implicit context: ContextSupport) =\n    (for {\n      contact <- contactsServices.findContactByLookupKey(lookupKey)\n    } yield contact).leftMap(mapServicesException)\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/device/impl/DeviceProcessDependencies.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device.impl\n\nimport cards.nine.services.api.ApiServices\nimport cards.nine.services.apps.AppsServices\nimport cards.nine.services.calls.CallsServices\nimport cards.nine.services.contacts.ContactsServices\nimport cards.nine.services.image.ImageServices\nimport cards.nine.services.persistence.PersistenceServices\nimport cards.nine.services.shortcuts.ShortcutsServices\nimport cards.nine.services.widgets.WidgetsServices\n\ntrait DeviceProcessDependencies {\n\n  val appsServices: AppsServices\n  val apiServices: ApiServices\n  val persistenceServices: PersistenceServices\n  val shortcutsServices: ShortcutsServices\n  val contactsServices: ContactsServices\n  val imageServices: ImageServices\n  val widgetsServices: WidgetsServices\n  val callsServices: CallsServices\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/device/impl/DeviceProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device.impl\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.process.device._\nimport cards.nine.services.api._\nimport cards.nine.services.apps.AppsServices\nimport cards.nine.services.calls.CallsServices\nimport cards.nine.services.contacts.ContactsServices\nimport cards.nine.services.image._\nimport cards.nine.services.persistence.{ImplicitsPersistenceServiceExceptions, PersistenceServices}\nimport cards.nine.services.shortcuts.ShortcutsServices\nimport cards.nine.services.widgets.WidgetsServices\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.services.connectivity.ConnectivityServices\n\nclass DeviceProcessImpl(\n    val appsServices: AppsServices,\n    val apiServices: ApiServices,\n    val persistenceServices: PersistenceServices,\n    val shortcutsServices: ShortcutsServices,\n    val contactsServices: ContactsServices,\n    val imageServices: ImageServices,\n    val widgetsServices: WidgetsServices,\n    val callsServices: CallsServices,\n    val connectivityServices: ConnectivityServices)\n    extends DeviceProcess\n    with DeviceProcessDependencies\n    with AppsDeviceProcessImpl\n    with ContactsDeviceProcessImpl\n    with DockAppsDeviceProcessImpl\n    with LastCallsDeviceProcessImpl\n    with ResetProcessImpl\n    with ShortcutsDeviceProcessImpl\n    with WidgetsDeviceProcessImpl\n    with ImplicitsDeviceException\n    with ImplicitsImageExceptions\n    with ImplicitsPersistenceServiceExceptions {\n\n  def getConfiguredNetworks(implicit context: ContextSupport): TaskService[Seq[String]] =\n    connectivityServices.getConfiguredNetworks.resolve[DeviceException]\n\n  def getPairedBluetoothDevices(implicit context: ContextSupport): TaskService[Seq[String]] =\n    connectivityServices.getPairedDevices.map(_.map(_.name)).resolve[DeviceException]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/device/impl/DockAppsDeviceProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.{AppDockType, DockType}\nimport cards.nine.models.{\n  ApplicationData,\n  DockAppData,\n  NineCardsIntent,\n  NineCardsIntentConversions\n}\nimport cards.nine.process.device._\nimport cards.nine.services.persistence.ImplicitsPersistenceServiceExceptions\n\ntrait DockAppsDeviceProcessImpl extends DeviceProcess with NineCardsIntentConversions {\n\n  self: DeviceProcessDependencies\n    with ImplicitsDeviceException\n    with ImplicitsPersistenceServiceExceptions =>\n\n  def generateDockApps(size: Int)(implicit context: ContextSupport) =\n    (for {\n      allDefaultApps <- appsServices.getDefaultApps\n      defaultApps    <- persistenceServices.fetchAppByPackages(allDefaultApps map (_.packageName))\n      images = defaultApps map (app => (app.packageName, \"\"))\n      apps   = matchAppsWithImages(allDefaultApps, images).take(size)\n      dockAppsData = apps map (app =>\n                                 DockAppData(\n                                   app.name,\n                                   AppDockType,\n                                   app.intent,\n                                   app.imagePath,\n                                   app.position))\n      dockApps <- persistenceServices.createOrUpdateDockApp(dockAppsData)\n    } yield dockApps).resolve[DockAppException]\n\n  def createOrUpdateDockApp(\n      name: String,\n      dockType: DockType,\n      intent: NineCardsIntent,\n      imagePath: String,\n      position: Int) =\n    (for {\n      _ <- persistenceServices.createOrUpdateDockApp(\n        Seq(DockAppData(name, dockType, intent, imagePath, position)))\n    } yield ()).resolve[DockAppException]\n\n  def saveDockApps(items: Seq[DockAppData]) =\n    (for {\n      dockApps <- persistenceServices.createOrUpdateDockApp(items)\n    } yield dockApps).resolve[DockAppException]\n\n  def getDockApps =\n    (for {\n      apps <- persistenceServices.fetchDockApps\n    } yield apps).resolve[DockAppException]\n\n  def deleteAllDockApps() =\n    (for {\n      _ <- persistenceServices.deleteAllDockApps()\n    } yield ()).resolve[DockAppException]\n\n  def deleteDockAppByPosition(position: Int) =\n    (for {\n      _ <- persistenceServices.deleteDockAppByPosition(position)\n    } yield ()).resolve[DockAppException]\n\n  private[this] def matchAppsWithImages(apps: Seq[ApplicationData], images: Seq[(String, String)])(\n      implicit context: ContextSupport): Seq[DockAppData] = {\n    apps.zipWithIndex.map {\n      case (app, index) =>\n        val image = images find (i => i._1 == app.packageName) map (_._2)\n        DockAppData(\n          name = app.packageName,\n          dockType = AppDockType,\n          intent = toNineCardIntent(app),\n          imagePath = image getOrElse \"\",\n          position = index)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/device/impl/LastCallsDeviceProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device.impl\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{Call, Contact}\nimport cards.nine.process.device._\nimport cards.nine.models.LastCallsContact\nimport cards.nine.services.calls.CallsServicesPermissionException\nimport cats.syntax.either._\nimport monix.eval.Task\n\ntrait LastCallsDeviceProcessImpl extends DeviceProcess {\n  self: DeviceProcessDependencies with ImplicitsDeviceException =>\n\n  def getLastCalls(implicit context: ContextSupport) = {\n\n    def simpleGroupCalls(lastCalls: Seq[Call]): TaskService[Seq[LastCallsContact]] = TaskService {\n      Task {\n        Either.right {\n\n          val defaultDate = 0L\n\n          def toSimpleLastCallsContact(number: String, calls: Seq[Call]): LastCallsContact = {\n            val (hasContact, name, date) = calls.headOption map { call =>\n              (call.name.isDefined, call.name getOrElse number, call.date)\n            } getOrElse (false, number, defaultDate)\n            LastCallsContact(\n              hasContact = hasContact,\n              number = number,\n              title = name,\n              lastCallDate = date,\n              calls = calls)\n          }\n\n          (lastCalls groupBy (_.number) map { case (k, v) => toSimpleLastCallsContact(k, v) }).toSeq\n        }\n      }\n    }\n\n    def combineContact(\n        lastCallsContact: LastCallsContact): TaskService[(LastCallsContact, Option[Contact])] =\n      for {\n        contact <- contactsServices.fetchContactByPhoneNumber(lastCallsContact.number)\n      } yield (lastCallsContact, contact)\n\n    def getCombinedContacts(\n        items: Seq[LastCallsContact]): TaskService[Seq[(LastCallsContact, Option[Contact])]] =\n      TaskService {\n        val tasks = items map (item => combineContact(item).value)\n        Task.gatherUnordered(tasks) map { list =>\n          Either.right(list.collect { case Right(combinedContact) => combinedContact })\n        }\n      }\n\n    def fillCombinedContacts(\n        combinedContacts: Seq[(LastCallsContact, Option[Contact])]): Seq[LastCallsContact] =\n      (combinedContacts map { combinedContact =>\n        val (lastCallsContact, maybeContact) = combinedContact\n        maybeContact map { contact =>\n          lastCallsContact.copy(\n            lookupKey = Some(contact.lookupKey),\n            photoUri = Some(contact.photoUri)\n          )\n        } getOrElse lastCallsContact\n      }).sortWith(_.lastCallDate > _.lastCallDate)\n\n    def mapServicesException[E >: NineCardException]: (NineCardException => E) = {\n      case e: CallsServicesPermissionException => CallPermissionException(e.message, Some(e))\n      case e                                   => CallException(e.getMessage, Option(e))\n    }\n\n    for {\n      lastCalls        <- callsServices.getLastCalls.leftMap(mapServicesException)\n      simpleGroupCalls <- simpleGroupCalls(lastCalls)\n      combinedContacts <- getCombinedContacts(simpleGroupCalls)\n    } yield fillCombinedContacts(combinedContacts)\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/device/impl/ResetProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.process.device.{DeviceProcess, ImplicitsDeviceException, ResetException}\nimport cards.nine.services.persistence.ImplicitsPersistenceServiceExceptions\n\ntrait ResetProcessImpl extends DeviceProcess {\n\n  self: DeviceProcessDependencies\n    with ImplicitsDeviceException\n    with ImplicitsPersistenceServiceExceptions =>\n\n  def resetSavedItems() =\n    (for {\n      _ <- persistenceServices.deleteAllWidgets()\n      _ <- persistenceServices.deleteAllCollections()\n      _ <- persistenceServices.deleteAllCards()\n      _ <- persistenceServices.deleteAllDockApps()\n    } yield ()).resolve[ResetException]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/device/impl/ShortcutsDeviceProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device.impl\n\nimport android.content.Intent.ShortcutIconResource\nimport android.graphics.Bitmap\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.IconResize\nimport cards.nine.process.device.{DeviceProcess, ImplicitsDeviceException, ShortcutException}\n\ntrait ShortcutsDeviceProcessImpl extends DeviceProcess {\n\n  self: DeviceProcessDependencies with ImplicitsDeviceException =>\n\n  def getAvailableShortcuts(implicit context: ContextSupport) =\n    (for {\n      shortcuts <- shortcutsServices.getShortcuts\n    } yield shortcuts).resolve[ShortcutException]\n\n  def saveShortcutIcon(bitmap: Bitmap, iconResize: Option[IconResize] = None)(\n      implicit context: ContextSupport) =\n    (for {\n      bitmapPath <- imageServices.saveBitmap(\n        bitmap,\n        iconResize map (_.width),\n        iconResize map (_.height))\n    } yield bitmapPath.path).resolve[ShortcutException]\n\n  def decodeShortcutIcon(resource: ShortcutIconResource)(\n      implicit context: ContextSupport): TaskService[Bitmap] =\n    imageServices.decodeShortcutIconResource(resource).resolve[ShortcutException]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/device/impl/WidgetsDeviceProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.OrderByName\nimport cards.nine.models.{AppWidget, Application}\nimport cards.nine.models.AppsWithWidgets\nimport cards.nine.process.device.{DeviceProcess, ImplicitsDeviceException, WidgetException}\n\ntrait WidgetsDeviceProcessImpl extends DeviceProcess {\n\n  self: DeviceProcessDependencies with ImplicitsDeviceException =>\n\n  def getWidgets(implicit context: ContextSupport) = {\n\n    def toAppsWithWidgets(apps: Seq[Application], widgets: Seq[AppWidget]): Seq[AppsWithWidgets] =\n      apps map { app =>\n        AppsWithWidgets(\n          packageName = app.packageName,\n          name = app.name,\n          widgets = widgets filter (_.packageName == app.packageName)\n        )\n      }\n\n    (for {\n      widgets <- widgetsServices.getWidgets\n      widgetsSorted = widgets sortBy (_.label)\n      apps <- persistenceServices.fetchApps(OrderByName)\n      packageNames    = widgetsSorted.map(_.packageName).distinct\n      appsWithWidgets = apps filter (app => packageNames.contains(app.packageName))\n    } yield toAppsWithWidgets(appsWithWidgets, widgetsSorted)).resolve[WidgetException]\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/intents/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.intents\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class LauncherExecutorProcessException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class LauncherExecutorProcessPermissionException(\n    message: String,\n    cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/intents/LauncherExecutorProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.intents\n\nimport cards.nine.commons.contexts.ActivityContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.NineCardsIntent\n\ntrait LauncherExecutorProcess {\n\n  /**\n   * Executes a NineCardIntent\n   *\n   * @param intent the intent\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception or\n   *         an IntentLauncherServicesPermissionException if this exception is a SecurityException\n   */\n  def execute(intent: NineCardsIntent)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Launch the contact preview based on a lookup key\n   *\n   * @param contactLookupKey the lookup key\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception\n   */\n  def executeContact(contactLookupKey: String)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Launch a share intent with the title and text specified\n   *\n   * @param text the text\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception\n   */\n  def launchShare(text: String)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Launch the search intent\n   *\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception\n   */\n  def launchSearch(implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Launch the Google weather intent\n   *\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception\n   */\n  def launchGoogleWeather(implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Launch the voice search intent\n   *\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception\n   */\n  def launchVoiceSearch(implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Launch the settings for a specific application\n   *\n   * @param packageName the application package\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception\n   */\n  def launchSettings(packageName: String)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Launch the intent for uninstall for a specific application\n   *\n   * @param packageName the application package\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception\n   */\n  def launchUninstall(packageName: String)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Launch the phone dial\n   *\n   * @param phoneNumber an optional phone number that will be sent to the dial activity\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception\n   */\n  def launchDial(phoneNumber: Option[String] = None)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Launch the intent for the Google Play Store\n   *\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception\n   */\n  def launchPlayStore(implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Launch a specific application\n   *\n   * @param packageName the application package\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception\n   */\n  def launchApp(packageName: String)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Launch the Google Play Store view for a specific application\n   *\n   * @param packageName the application package\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception\n   */\n  def launchGooglePlay(packageName: String)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Launch the intent for an url\n   *\n   * @param url the web url\n   * @return A TaskService[Unit] with an IntentLauncherServicesException if the internal API throws an Exception\n   */\n  def launchUrl(url: String)(implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/intents/impl/LauncherExecutorProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.intents.impl\n\nimport cards.nine.commons.contexts.ActivityContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.NineCardsIntentExtras._\nimport cards.nine.models._\nimport cards.nine.process.intents.{\n  LauncherExecutorProcess,\n  LauncherExecutorProcessException,\n  LauncherExecutorProcessPermissionException\n}\nimport cards.nine.services.intents.{\n  IntentLauncherServicesPermissionException,\n  LauncherIntentServices\n}\nimport cats.data.EitherT\nimport cats.syntax.either._\nimport monix.eval.Task\n\nclass LauncherExecutorProcessImpl(\n    config: LauncherExecutorProcessConfig,\n    launcherIntentServices: LauncherIntentServices)\n    extends LauncherExecutorProcess {\n\n  override def execute(intent: NineCardsIntent)(implicit activityContext: ActivityContextSupport) = {\n\n    def createAppAction: Option[IntentAction] =\n      for {\n        packageName <- intent.extractPackageName()\n        className   <- intent.extractClassName()\n      } yield {\n        AppAction(packageName, className)\n      }\n\n    def createLaunchAppAction: Option[IntentAction] =\n      intent.extractPackageName() map AppLauncherAction\n\n    def createGooglePlayAppAction: Option[IntentAction] =\n      intent.extractPackageName() map (packageName =>\n                                         AppGooglePlayAction(config.googlePlayUrl, packageName))\n\n    def createSmsAction: Option[IntentAction] =\n      intent.extractPhone() map PhoneSmsAction\n\n    def createCallAction: Option[IntentAction] =\n      intent.extractPhone() map PhoneCallAction\n\n    def createEmailAction: Option[IntentAction] =\n      intent.extractEmail() map (email => EmailAction(email, config.titleEmailDialogChooser))\n\n    intent.getAction match {\n      case `openApp` =>\n        tryLaunchIntentService(createAppAction)\n          .recoverWith(verifyPermissionExceptionOrTry(createLaunchAppAction))\n          .recoverWith(verifyPermissionExceptionOrTry(createGooglePlayAppAction))\n      case `openNoInstalledApp` =>\n        tryLaunchIntentService(createGooglePlayAppAction)\n      case `openSms` =>\n        tryLaunchIntentService(createSmsAction)\n      case `openPhone` =>\n        tryLaunchIntentService(createCallAction)\n      case `openEmail` =>\n        tryLaunchIntentService(createEmailAction)\n      case `openContact` =>\n        intent.extractLookup() match {\n          case Some(lookupKey) => executeContact(lookupKey)\n          case None =>\n            TaskService(\n              Task(\n                Either.left(LauncherExecutorProcessException(\"Contact lookup not found\", None))))\n        }\n      case _ =>\n        launcherIntentServices.launchIntent(intent.toIntent).leftMap(mapServicesException)\n    }\n  }\n\n  override def executeContact(lookupKey: String)(\n      implicit activityContext: ActivityContextSupport) =\n    toProcessServiceAction(ContactAction(lookupKey))\n\n  override def launchShare(text: String)(implicit activityContext: ActivityContextSupport) =\n    toProcessServiceAction(ShareAction(text, config.titleShareDialogChooser))\n\n  override def launchSearch(implicit activityContext: ActivityContextSupport) =\n    toProcessServiceAction(SearchGlobalAction).recoverWith(\n      verifyPermissionExceptionOrTry(Some(SearchWebAction)))\n\n  override def launchGoogleWeather(implicit activityContext: ActivityContextSupport) =\n    toProcessServiceAction(GoogleWeatherAction)\n\n  override def launchVoiceSearch(implicit activityContext: ActivityContextSupport) =\n    toProcessServiceAction(SearchVoiceAction)\n\n  override def launchSettings(packageName: String)(\n      implicit activityContext: ActivityContextSupport) =\n    toProcessServiceAction(AppSettingsAction(packageName))\n      .recoverWith(verifyPermissionExceptionOrTry(Some(GlobalSettingsAction)))\n\n  override def launchUninstall(packageName: String)(\n      implicit activityContext: ActivityContextSupport) =\n    toProcessServiceAction(AppUninstallAction(packageName))\n\n  override def launchDial(phoneNumber: Option[String])(\n      implicit activityContext: ActivityContextSupport) =\n    toProcessServiceAction(PhoneDialAction(phoneNumber))\n\n  override def launchPlayStore(implicit activityContext: ActivityContextSupport) =\n    toProcessServiceAction(GooglePlayStoreAction)\n\n  override def launchApp(packageName: String)(implicit activityContext: ActivityContextSupport) =\n    toProcessServiceAction(AppLauncherAction(packageName))\n\n  override def launchGooglePlay(packageName: String)(\n      implicit activityContext: ActivityContextSupport) =\n    toProcessServiceAction(AppGooglePlayAction(config.googlePlayUrl, packageName))\n\n  override def launchUrl(url: String)(implicit activityContext: ActivityContextSupport) =\n    toProcessServiceAction(UrlAction(url))\n\n  private[this] def mapServicesException[E >: NineCardException]: (NineCardException => E) = {\n    case e: IntentLauncherServicesPermissionException =>\n      LauncherExecutorProcessPermissionException(e.message, Some(e))\n    case e =>\n      LauncherExecutorProcessException(e.message, Some(e))\n  }\n\n  private[this] def toProcessServiceAction(action: IntentAction)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit] =\n    launcherIntentServices.launchIntentAction(action).leftMap(mapServicesException)\n\n  private[this] def tryLaunchIntentService(maybeAction: Option[IntentAction])(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit] =\n    maybeAction match {\n      case Some(action) => toProcessServiceAction(action)\n      case None =>\n        TaskService {\n          Task(\n            Either.left(\n              LauncherExecutorProcessException(s\"Not suitable intent for this NineCardIntent\")))\n        }\n    }\n\n  private[this] def verifyPermissionExceptionOrTry[A](f: => Option[IntentAction])(\n      implicit activityContext: ActivityContextSupport): PartialFunction[\n    NineCardException,\n    EitherT[Task, NineCardException, Unit]] = {\n    case e: LauncherExecutorProcessPermissionException => TaskService(Task(Either.left(e)))\n    case _                                             => tryLaunchIntentService(f)\n  }\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/moment/MomentException.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.moment\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class MomentException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsMomentException {\n  implicit def momentException = (t: Throwable) => MomentException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/moment/MomentProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.moment\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.types.{KindActivity, NineCardsMoment}\nimport cards.nine.models.{Moment, MomentData}\n\ntrait MomentProcess {\n\n  /**\n   * Gets the existing moments\n   *\n   * @return the Seq[cards.nine.models.Moment]\n   * @throws MomentException if there was an error getting the existing moments\n   */\n  def getMoments: TaskService[Seq[Moment]]\n\n  /**\n   * Gets a moment by the collectionId\n   *\n   * @param collectionId the id of the collection related\n   * @return the Option[cards.nine.models.Moment]\n   * @throws MomentException if there was an error getting the existing moments\n   */\n  def getMomentByCollectionId(collectionId: Int): TaskService[Option[Moment]]\n\n  /**\n   * Get moment by type, if the moment don't exist return an exception\n   *\n   * @param momentType type of moment\n   * @return the cards.nine.models.Moment\n   * @throws MomentException if there was an error getting the existing moments\n   */\n  def getMomentByType(momentType: NineCardsMoment): TaskService[Moment]\n\n  /**\n   * Get moment by type, if the moment don't exist return None\n   *\n   * @param momentType type of moment\n   * @return the Option[cards.nine.models.Moment]\n   * @throws MomentException if there was an error getting the existing moments\n   */\n  @deprecated\n  def fetchMomentByType(momentType: NineCardsMoment): TaskService[Option[Moment]]\n\n  /**\n   * Get moment by id, if the moment don't exist return None\n   *\n   * @param momentId id of moment\n   * @return the Option[cards.nine.models.Moment]\n   * @throws MomentException if there was an error getting the existing moments\n   */\n  def findMoment(momentId: Int): TaskService[Option[Moment]]\n\n  /**\n   * Creates Moments from some already formed and given Moments\n   *\n   * @param moment the cards.nine.models.Moment of Moments\n   * @throws MomentException if there was an error creating the moments' collections\n   */\n  def updateMoment(moment: Moment)(implicit context: ContextSupport): TaskService[Unit]\n\n  /**\n   * Creates Moments from some already formed and given Moments\n   *\n   * @param moments sequence of of cards.nine.models.MomentData\n   * @return the List[cards.nine.models.Moment]\n   * @throws MomentException if there was an error creating the moments' collections\n   */\n  def saveMoments(moments: Seq[MomentData])(\n      implicit context: ContextSupport): TaskService[Seq[Moment]]\n\n  /**\n   * Delete moment in database\n   *\n   * @throws MomentException if exist some problem to get the app or storing it\n   */\n  def deleteMoment(momentId: Int): TaskService[Unit]\n\n  /**\n   * Delete all moments in database\n   *\n   * @throws MomentException if exist some problem to get the app or storing it\n   */\n  def deleteAllMoments(): TaskService[Unit]\n\n  /**\n   * Gets the best available moments\n   *\n   * @return the best Moment or None if the database is empty\n   * @throws MomentException if there was an error getting the best moment\n   */\n  def getBestAvailableMoment(\n      maybeHeadphones: Option[Boolean] = None,\n      maybeActivity: Option[KindActivity] = None)(\n      implicit context: ContextSupport): TaskService[Option[Moment]]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/moment/impl/MomentProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.moment.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models._\nimport cards.nine.models.types._\nimport cards.nine.process.moment._\nimport cards.nine.services.awareness.AwarenessServices\nimport cards.nine.services.persistence._\nimport cards.nine.services.connectivity.ConnectivityServices\nimport org.joda.time.DateTime\nimport org.joda.time.DateTimeConstants._\nimport org.joda.time.format.DateTimeFormat\n\nclass MomentProcessImpl(\n    val persistenceServices: PersistenceServices,\n    val connectivityServices: ConnectivityServices,\n    val awarenessServices: AwarenessServices)\n    extends MomentProcess\n    with ImplicitsMomentException\n    with ImplicitsPersistenceServiceExceptions {\n\n  override def getMoments = persistenceServices.fetchMoments.resolve[MomentException]\n\n  override def getMomentByCollectionId(collectionId: Int) =\n    persistenceServices.getMomentByCollectionId(collectionId).resolve[MomentException]\n\n  override def getMomentByType(momentType: NineCardsMoment) =\n    persistenceServices.getMomentByType(momentType).resolve[MomentException]\n\n  override def fetchMomentByType(momentType: NineCardsMoment) =\n    persistenceServices.fetchMomentByType(momentType.name).resolve[MomentException]\n\n  override def findMoment(momentId: Int) =\n    persistenceServices.fetchMomentById(momentId).resolve[MomentException]\n\n  override def updateMoment(moment: Moment)(implicit context: ContextSupport) =\n    (for {\n      _ <- persistenceServices.updateMoment(moment)\n    } yield ()).resolve[MomentException]\n\n  override def saveMoments(moments: Seq[MomentData])(implicit context: ContextSupport) =\n    (for {\n      moments <- persistenceServices.addMoments(moments)\n    } yield moments).resolve[MomentException]\n\n  override def deleteMoment(momentId: Int): TaskService[Unit] =\n    (for {\n      _ <- persistenceServices.deleteMoment(momentId)\n    } yield ()).resolve[MomentException]\n\n  override def deleteAllMoments() =\n    (for {\n      _ <- persistenceServices.deleteAllMoments()\n    } yield ()).resolve[MomentException]\n\n  def getBestAvailableMoment(\n      maybeHeadphones: Option[Boolean] = None,\n      maybeActivity: Option[KindActivity] = None)(implicit context: ContextSupport) = {\n\n    val now = getNowDateTime\n\n    def isHappening(moment: Moment): Boolean = moment.timeslot exists { slot =>\n      val (fromSlot, toSlot) = toDateTime(now, slot)\n      fromSlot.isBefore(now) && toSlot\n        .isAfter(now) && slot.days.lift(getDayOfWeek(now)).contains(1)\n    }\n\n    def prioritizedMomentsByTime(moment1: Moment, moment2: Moment): Boolean = {\n\n      def prioritizedByTime(): Boolean = {\n        val sum1 = (moment1.timeslot map { slot =>\n          val (fromSlot, toSlot) = toDateTime(now, slot)\n          toSlot.getMillis - fromSlot.getMillis\n        }).sum\n        val sum2 = (moment2.timeslot map { slot =>\n          val (fromSlot, toSlot) = toDateTime(now, slot)\n          toSlot.getMillis - fromSlot.getMillis\n        }).sum\n        sum1 < sum2\n      }\n\n      (isHappening(moment1), isHappening(moment2)) match {\n        case (true, false)        => true\n        case (false, true)        => false\n        case (h1, h2) if h1 == h2 => prioritizedByTime()\n        case _                    => false\n      }\n    }\n\n    def headphonesMoment(moments: Seq[Moment]): TaskService[Option[Moment]] =\n      (moments.find(_.momentType == MusicMoment), maybeHeadphones) match {\n        case (Some(m), Some(hp)) => TaskService.right(if (hp) Some(m) else None)\n        case (Some(m), None) =>\n          awarenessServices.getHeadphonesState\n            .resolveLeftTo(Headphones(false))\n            .map(headphones => if (headphones.connected) Some(m) else None)\n        case (None, _) => TaskService.right(None)\n      }\n\n    def wifiMoment(moments: Seq[Moment]): TaskService[Option[Moment]] =\n      connectivityServices.getCurrentSSID.map {\n        case Some(ssid) =>\n          (moments filter (_.wifi.contains(ssid)) sortWith prioritizedMomentsByTime).headOption\n        case None => None\n      }\n\n    def bluetoothMoment(moments: Seq[Moment]): TaskService[Option[Moment]] =\n      connectivityServices.getBluetoothConnected.map { devices =>\n        (moments filter (_.bluetooth\n          .exists(b => devices.contains(b))) sortWith prioritizedMomentsByTime).headOption\n      }\n\n    def activityMoment(moments: Seq[Moment]): TaskService[Option[Moment]] = {\n\n      def activityMatch(momentType: NineCardsMoment, activity: KindActivity): Boolean =\n        momentType == CarMoment && activity == InVehicleActivity\n\n      val activityMoments = moments\n        .map(moment => (moment.momentType, moment))\n        .filter(tuple => NineCardsMoment.activityMoments.contains(tuple._1))\n\n      (activityMoments.isEmpty, maybeActivity) match {\n        case (true, _) => TaskService.right(None)\n        case (false, Some(activity)) =>\n          TaskService.right(\n            activityMoments.find(tuple => activityMatch(tuple._1, activity)).map(_._2))\n        case _ =>\n          awarenessServices.getTypeActivity.resolveLeftTo(ProbablyActivity(UnknownActivity)).map {\n            activity =>\n              activityMoments\n                .find(tuple => activityMatch(tuple._1, activity.activityType))\n                .map(_._2)\n          }\n      }\n    }\n\n    def hourMoment(moments: Seq[Moment]): TaskService[Option[Moment]] = TaskService.right {\n      (moments.filter { moment =>\n        moment.wifi.isEmpty &&\n        NineCardsMoment.hourlyMoments.contains(moment.momentType) &&\n        isHappening(moment)\n      } sortWith prioritizedMomentsByTime).headOption\n    }\n\n    def defaultMoment(moments: Seq[Moment]): TaskService[Moment] = {\n      moments.find(_.momentType.isDefault) match {\n        case Some(moment) => TaskService.right(moment)\n        case _ =>\n          val momentData = MomentData(\n            collectionId = None,\n            timeslot = Seq.empty,\n            wifi = Seq.empty,\n            bluetooth = Seq.empty,\n            headphone = false,\n            momentType = NineCardsMoment.defaultMoment)\n          persistenceServices.addMoment(momentData)\n      }\n    }\n\n    def bestChoice(moments: Seq[Moment]): TaskService[Option[Moment]] = {\n      val momentsToEvaluate = moments.filterNot(_.momentType.isDefault)\n      Seq(headphonesMoment(_), bluetoothMoment(_), wifiMoment(_), activityMoment(_), hourMoment(_))\n        .foldLeft[TaskService[Option[Moment]]](TaskService.right(None)) { (s1, s2) =>\n          s1.flatMap(maybeMoment =>\n            if (maybeMoment.isDefined) TaskService.right(maybeMoment) else s2(momentsToEvaluate))\n        }\n    }\n\n    def checkEmptyMoments(moments: Seq[Moment]): TaskService[Option[Moment]] =\n      if (moments.nonEmpty) {\n        for {\n          maybeBestMoment <- bestChoice(moments)\n          moment          <- maybeBestMoment map TaskService.right getOrElse defaultMoment(moments)\n        } yield Option(moment)\n      } else TaskService.right(None)\n\n    for {\n      moments     <- persistenceServices.fetchMoments\n      maybeMoment <- checkEmptyMoments(moments)\n    } yield maybeMoment\n  }\n\n  protected def getNowDateTime = DateTime.now()\n\n  protected def getDayOfWeek(now: DateTime) =\n    now.getDayOfWeek match {\n      case SUNDAY    => 0\n      case MONDAY    => 1\n      case TUESDAY   => 2\n      case WEDNESDAY => 3\n      case THURSDAY  => 4\n      case FRIDAY    => 5\n      case SATURDAY  => 6\n    }\n\n  private[this] def toDateTime(now: DateTime, timeslot: MomentTimeSlot): (DateTime, DateTime) = {\n\n    val formatter = DateTimeFormat.forPattern(\"HH:mm\")\n\n    val from = formatter.parseDateTime(timeslot.from)\n    val to   = formatter.parseDateTime(timeslot.to)\n\n    val fromDT = now.withTime(from.getHourOfDay, from.getMinuteOfHour, 0, 0)\n    val toDT   = now.withTime(to.getHourOfDay, to.getMinuteOfHour, 0, 0)\n\n    (fromDT, toDT)\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/recognition/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.recognition\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class RecognitionProcessException(\n    message: String,\n    cause: Option[Throwable] = None,\n    recoverable: Boolean = false)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsRecognitionProcessExceptions {\n  implicit def recognitionProcessExceptionConverter =\n    (t: Throwable) => RecognitionProcessException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/recognition/RecognitionProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.recognition\n\nimport android.content.BroadcastReceiver\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{Headphones, Location, ProbablyActivity, WeatherState}\n\ntrait RecognitionProcess {\n\n  /**\n   * Get most probably activity: in vehicle, walking, running, etc\n   *\n   * @return ProbablyActivity\n   */\n  def getMostProbableActivity: TaskService[ProbablyActivity]\n\n  /**\n   * Register a pending intent for fence updates\n   * @param action the action for the intent\n   * @param receiver that will receive the updates\n   */\n  def registerFenceUpdates(action: String, receiver: BroadcastReceiver)(\n      implicit contextSupport: ContextSupport): TaskService[Unit]\n\n  /**\n   * Register a pending intent for fence updates\n   * @param action the action for the intent\n   */\n  def unregisterFenceUpdates(action: String)(\n      implicit contextSupport: ContextSupport): TaskService[Unit]\n\n  /**\n   * Get if the headphones are connected\n   *\n   * @return Headphones\n   */\n  def getHeadphone: TaskService[Headphones]\n\n  /**\n   * Get the current location\n   *\n   * @return Location\n   */\n  def getLocation(implicit contextSupport: ContextSupport): TaskService[Location]\n\n  /**\n   * Get the current weather\n   *\n   * @return WeatherState\n   */\n  def getWeather: TaskService[WeatherState]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/recognition/impl/RecognitionProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.recognition.impl\n\nimport android.content.BroadcastReceiver\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models._\nimport cards.nine.models.types._\nimport cards.nine.process.recognition._\nimport cards.nine.services.awareness.AwarenessServices\nimport cards.nine.services.persistence.PersistenceServices\n\nclass RecognitionProcessImpl(\n    persistenceServices: PersistenceServices,\n    awarenessServices: AwarenessServices)\n    extends RecognitionProcess\n    with ImplicitsRecognitionProcessExceptions {\n\n  override def getMostProbableActivity: TaskService[ProbablyActivity] =\n    awarenessServices.getTypeActivity.resolve[RecognitionProcessException]\n\n  override def registerFenceUpdates(action: String, receiver: BroadcastReceiver)(\n      implicit contextSupport: ContextSupport) = {\n\n    def getFencesFromMoments(moments: Seq[Moment]): Seq[AwarenessFenceUpdate] =\n      moments.map(_.momentType).flatMap {\n        case MusicMoment => Some(HeadphonesFence)\n        case CarMoment   => Some(InVehicleFence)\n        case _           => None\n      }\n\n    (for {\n      moments <- persistenceServices.fetchMoments\n      fences = getFencesFromMoments(moments)\n      _ <- if (fences.nonEmpty) awarenessServices.registerFenceUpdates(action, fences, receiver)\n      else TaskService.empty\n    } yield ()).resolve[RecognitionProcessException]\n  }\n\n  override def unregisterFenceUpdates(action: String)(\n      implicit contextSupport: ContextSupport): TaskService[Unit] =\n    awarenessServices.unregisterFenceUpdates(action).resolve[RecognitionProcessException]\n\n  override def getHeadphone: TaskService[Headphones] =\n    awarenessServices.getHeadphonesState.resolve[RecognitionProcessException]\n\n  override def getLocation(implicit contextSupport: ContextSupport): TaskService[Location] =\n    awarenessServices.getLocation.resolve[RecognitionProcessException]\n\n  override def getWeather: TaskService[WeatherState] =\n    awarenessServices.getWeather.resolve[RecognitionProcessException]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/recommendations/RecommendationsExceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.recommendations\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class RecommendedAppsException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class RecommendedAppsConfigurationException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/recommendations/RecommendationsProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.recommendations\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.NotCategorizedPackage\nimport cards.nine.models.types.NineCardsCategory\n\ntrait RecommendationsProcess {\n\n  /**\n   * Get recommended apps based on a category\n   *\n   * @param category a valid category identification\n   * @return the Seq[NotCategorizedPackage]\n   * @throws RecommendedAppsConfigurationException if there was an error with the API configuration\n   * @throws RecommendedAppsException if there was an error fetching the recommended apps\n   */\n  def getRecommendedAppsByCategory(\n      category: NineCardsCategory,\n      excludePackages: Seq[String] = Seq.empty)(\n      implicit context: ContextSupport): TaskService[Seq[NotCategorizedPackage]]\n\n  /**\n   * Get recommended apps based on a category\n   *\n   * @param packages a valid list of packages\n   * @return the Seq[NotCategorizedPackage]\n   * @throws RecommendedAppsConfigurationException if there was an error with the API configuration\n   * @throws RecommendedAppsException if there was an error fetching the recommended apps\n   */\n  def getRecommendedAppsByPackages(\n      packages: Seq[String],\n      excludePackages: Seq[String] = Seq.empty)(\n      implicit context: ContextSupport): TaskService[Seq[NotCategorizedPackage]]\n\n  /**\n   * Search apps based on a query string\n   *\n   * @param query the query string\n   * @return the Seq[NotCategorizedPackage]\n   * @throws RecommendedAppsConfigurationException if there was an error with the API configuration\n   * @throws RecommendedAppsException if there was an error fetching the recommended apps\n   */\n  def searchApps(query: String, excludePackages: Seq[String] = Seq.empty)(\n      implicit context: ContextSupport): TaskService[Seq[NotCategorizedPackage]]\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/recommendations/impl/RecommendationsProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.recommendations.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.NotCategorizedPackage\nimport cards.nine.process.recommendations._\nimport cards.nine.process.utils.ApiUtils\nimport cards.nine.services.api.{ApiServiceConfigurationException, ApiServices}\nimport cards.nine.services.persistence.PersistenceServices\nimport cards.nine.models.types.NineCardsCategory\n\nclass RecommendationsProcessImpl(\n    apiServices: ApiServices,\n    persistenceServices: PersistenceServices)\n    extends RecommendationsProcess {\n\n  val apiUtils = new ApiUtils(persistenceServices)\n\n  val defaultRecommendedAppsLimit = 20\n  val defaultSearchAppsLimit      = 20\n\n  override def getRecommendedAppsByCategory(\n      category: NineCardsCategory,\n      excludePackages: Seq[String] = Seq.empty)(implicit context: ContextSupport) =\n    (for {\n      userConfig <- apiUtils.getRequestConfig\n      response <- apiServices.getRecommendedApps(\n        category.name,\n        excludePackages,\n        defaultRecommendedAppsLimit)(userConfig)\n    } yield response.seq).resolveLeft(mapLeft)\n\n  override def getRecommendedAppsByPackages(\n      packages: Seq[String],\n      excludePackages: Seq[String] = Seq.empty)(implicit context: ContextSupport) =\n    (for {\n      userConfig <- apiUtils.getRequestConfig\n      response <- apiServices.getRecommendedAppsByPackages(\n        packages,\n        excludePackages,\n        defaultRecommendedAppsLimit)(userConfig)\n    } yield response.seq).resolveLeft(mapLeft)\n\n  override def searchApps(query: String, excludePackages: Seq[String])(\n      implicit context: ContextSupport): TaskService[Seq[NotCategorizedPackage]] =\n    (for {\n      userConfig <- apiUtils.getRequestConfig\n      response <- apiServices.searchApps(query, excludePackages, defaultSearchAppsLimit)(\n        userConfig)\n    } yield response.seq).resolveLeft(mapLeft)\n\n  private[this] def mapLeft[T]: (NineCardException) => Either[NineCardException, T] = {\n    case e: ApiServiceConfigurationException =>\n      Left(RecommendedAppsConfigurationException(e.message, Some(e)))\n    case e => Left(RecommendedAppsException(e.message, Some(e)))\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/sharedcollections/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.sharedcollections\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class SharedCollectionsException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class SharedCollectionsConfigurationException(\n    message: String,\n    cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/sharedcollections/SharedCollectionsProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.sharedcollections\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models._\nimport cards.nine.models.types.{NineCardsCategory, TypeSharedCollection}\n\ntrait SharedCollectionsProcess {\n\n  /**\n   * Get a shared collection\n   *\n   * @param sharedCollectionId the shared collection identifier\n   * @return the SharedCollection\n   * @throws SharedCollectionsConfigurationException if there was an error with the API configuration\n   * @throws SharedCollectionsException if there was an error fetching the collection or it doesn't exists\n   */\n  def getSharedCollection(sharedCollectionId: String)(\n      implicit context: ContextSupport): TaskService[SharedCollection]\n\n  /**\n   * Get shared collections based on a category\n   *\n   * @param category a valid category identification\n   * @param typeShareCollection type of shared collection\n   * @param offset offset of query\n   * @param limit limit of query\n   * @return the Seq[cards.nine.models.SharedCollection]\n   * @throws SharedCollectionsConfigurationException if there was an error with the API configuration\n   * @throws SharedCollectionsException if there was an error fetching the collections\n   */\n  def getSharedCollectionsByCategory(\n      category: NineCardsCategory,\n      typeShareCollection: TypeSharedCollection,\n      offset: Int = 0,\n      limit: Int = 50)(implicit context: ContextSupport): TaskService[Seq[SharedCollection]]\n\n  /**\n   * Get published collections\n   *\n   * @return the Seq[cards.nine.models.SharedCollection]\n   * @throws SharedCollectionsConfigurationException if there was an error with the API configuration\n   * @throws SharedCollectionsException if there was an error fetching the published collections\n   */\n  def getPublishedCollections()(\n      implicit context: ContextSupport): TaskService[Seq[SharedCollection]]\n\n  /**\n   * Persist a SharedCollection\n   *\n   * @param name The name of the collection\n   * @param author The original author of the collection\n   * @param packages The list of packages in the collection\n   * @param category the NineCardsCategory of the SharedCollection\n   * @param icon The collection's icon\n   * @param community A flag for whether this is a community collection\n   * @return shared collection identifier\n   * @throws SharedCollectionsConfigurationException if there was an error with the API configuration\n   * @throws SharedCollectionsException if the service cannot create the collection for some reason\n   */\n  def createSharedCollection(\n      name: String,\n      author: String,\n      packages: Seq[String],\n      category: NineCardsCategory,\n      icon: String,\n      community: Boolean)(implicit context: ContextSupport): TaskService[String]\n\n  /**\n   * Updates a SharedCollection\n   *\n   * @param sharedCollectionId the SharedCollection id\n   * @param name the name of the SharedCollection\n   * @param packages the packages of the SharedCollection\n   * @return shared collection identifier\n   * @throws SharedCollectionsConfigurationException if there was an error with the API configuration\n   * @throws SharedCollectionsException if the service cannot create the collection for some reason\n   */\n  def updateSharedCollection(sharedCollectionId: String, name: String, packages: Seq[String])(\n      implicit context: ContextSupport): TaskService[String]\n\n  /**\n   * Gets all the subscriptions of the current user\n   *\n   * @return the Seq[cards.nine.models.Subscription]\n   * @throws SharedCollectionsConfigurationException if there was an error with the API configuration\n   * @throws SharedCollectionsException if the service cannot get the subscriptions or the collections\n   */\n  def getSubscriptions()(implicit context: ContextSupport): TaskService[Seq[Subscription]]\n\n  /**\n   * Subscribes to a public collection\n   *\n   * @param originalSharedCollectionId the public id of the collection to subscribe on\n   * @throws SharedCollectionsConfigurationException if there was an error with the API configuration\n   * @throws SharedCollectionsException if the service cannot subscribe to the collection\n   */\n  def subscribe(originalSharedCollectionId: String)(\n      implicit context: ContextSupport): TaskService[Unit]\n\n  /**\n   * Unsubscribes from a public collection\n   *\n   * @param originalSharedCollectionId the public id of the collection to unsubscribe from\n   * @throws SharedCollectionsConfigurationException if there was an error with the API configuration\n   * @throws SharedCollectionsException if the service cannot unsubscribe from the collection\n   */\n  def unsubscribe(originalSharedCollectionId: String)(\n      implicit context: ContextSupport): TaskService[Unit]\n\n  /**\n   * Updates the number of view inSharedCollection\n   *\n   * @param sharedCollectionId the SharedCollection id\n   * @return shared collection identifier\n   * @throws SharedCollectionsConfigurationException if there was an error with the API configuration\n   * @throws SharedCollectionsException if the service cannot updated the collection\n   */\n  def updateViewSharedCollection(sharedCollectionId: String)(\n      implicit context: ContextSupport): TaskService[Unit]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/sharedcollections/impl/SharedCollectionsProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.sharedcollections.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types._\nimport cards.nine.models.{Collection, _}\nimport cards.nine.process.sharedcollections._\nimport cards.nine.process.utils.ApiUtils\nimport cards.nine.services.api.{ApiServiceConfigurationException, ApiServices}\nimport cards.nine.services.persistence.PersistenceServices\n\nclass SharedCollectionsProcessImpl(\n    apiServices: ApiServices,\n    persistenceServices: PersistenceServices)\n    extends SharedCollectionsProcess {\n\n  val apiUtils = new ApiUtils(persistenceServices)\n\n  override def getSharedCollection(sharedCollectionId: String)(implicit context: ContextSupport) =\n    (for {\n      userConfig       <- apiUtils.getRequestConfig\n      sharedCollection <- apiServices.getSharedCollection(sharedCollectionId)(userConfig)\n      maybeCollection <- persistenceServices.fetchCollectionBySharedCollectionId(\n        sharedCollectionId)\n    } yield syncCollectionStatus(maybeCollection, sharedCollection)).resolveLeft(mapLeft)\n\n  override def getSharedCollectionsByCategory(\n      category: NineCardsCategory,\n      typeShareCollection: TypeSharedCollection,\n      offset: Int = 0,\n      limit: Int = 50)(implicit context: ContextSupport): TaskService[Seq[SharedCollection]] =\n    (for {\n      userConfig <- apiUtils.getRequestConfig\n      sharedCollections <- apiServices.getSharedCollectionsByCategory(\n        category.name,\n        typeShareCollection.name,\n        offset,\n        limit)(userConfig)\n      localCollectionMap <- fetchSharedCollectionMap(sharedCollections.map(_.sharedCollectionId))\n    } yield\n      sharedCollections map { sharedCollection =>\n        syncCollectionStatus(\n          localCollectionMap.get(sharedCollection.sharedCollectionId),\n          sharedCollection)\n      }).resolveLeft(mapLeft)\n\n  override def getPublishedCollections()(implicit context: ContextSupport) =\n    (for {\n      userConfig         <- apiUtils.getRequestConfig\n      sharedCollections  <- apiServices.getPublishedCollections()(userConfig)\n      localCollectionMap <- fetchSharedCollectionMap(sharedCollections.map(_.sharedCollectionId))\n    } yield\n      sharedCollections map { sharedCollection =>\n        syncCollectionStatus(\n          localCollectionMap.get(sharedCollection.sharedCollectionId),\n          sharedCollection)\n      }).resolveLeft(mapLeft)\n\n  private[this] def fetchSharedCollectionMap(\n      sharedCollectionsIds: Seq[String]): TaskService[Map[String, Collection]] =\n    for {\n      localCollections <- persistenceServices.fetchCollectionsBySharedCollectionIds(\n        sharedCollectionsIds)\n    } yield localCollections.flatMap(c => c.sharedCollectionId.map(id => id -> c)).toMap\n\n  private[this] def syncCollectionStatus(\n      maybeLocalCollection: Option[Collection],\n      sharedCol: SharedCollection): SharedCollection = maybeLocalCollection match {\n    case Some(c) =>\n      sharedCol.copy(locallyAdded = Some(true), publicCollectionStatus = c.publicCollectionStatus)\n    case None => sharedCol.copy(locallyAdded = Some(false))\n  }\n\n  override def createSharedCollection(\n      name: String,\n      author: String,\n      packages: Seq[String],\n      category: NineCardsCategory,\n      icon: String,\n      community: Boolean)(implicit context: ContextSupport) = {\n    (for {\n      userConfig <- apiUtils.getRequestConfig\n      sharedCollectionId <- apiServices.createSharedCollection(\n        name,\n        author,\n        packages,\n        category.name,\n        icon,\n        community)(userConfig)\n    } yield sharedCollectionId).resolveLeft(mapLeft)\n  }\n\n  override def updateSharedCollection(\n      sharedCollectionId: String,\n      name: String,\n      packages: Seq[String])(implicit context: ContextSupport) = {\n    (for {\n      userConfig <- apiUtils.getRequestConfig\n      sharedCollectionId <- apiServices.updateSharedCollection(\n        sharedCollectionId,\n        Option(name),\n        packages)(userConfig)\n    } yield sharedCollectionId).resolveLeft(mapLeft)\n  }\n\n  override def getSubscriptions()(implicit context: ContextSupport) = {\n\n    def toSubscription(subscriptions: (String, Collection)): Subscription = {\n      val (sharedCollectionId, collection) = subscriptions\n      Subscription(\n        id = collection.id,\n        sharedCollectionId = sharedCollectionId,\n        name = collection.name,\n        apps = collection.cards.count(card => card.cardType == AppCardType),\n        icon = collection.icon,\n        themedColorIndex = collection.themedColorIndex,\n        subscribed = collection.sharedCollectionSubscribed)\n    }\n\n    (for {\n      collections <- persistenceServices.fetchCollections\n    } yield {\n\n      val publicationsIds = collections filter { collection =>\n        collection.sharedCollectionId.isDefined & collection.originalSharedCollectionId != collection.sharedCollectionId\n      } flatMap (_.sharedCollectionId)\n\n      val collectionsWithOriginalSharedCollectionId: Seq[(String, Collection)] =\n        collections\n          .flatMap(collection => collection.originalSharedCollectionId.map((_, collection)))\n          .filter {\n            case (sharedCollectionId: String, _) => !publicationsIds.contains(sharedCollectionId)\n          }\n\n      (collectionsWithOriginalSharedCollectionId map {\n        case (sharedCollectionId: String, collection: Collection) =>\n          (sharedCollectionId, collection)\n      }) map toSubscription\n\n    }).resolveLeft(mapLeft)\n  }\n\n  override def subscribe(sharedCollectionId: String)(implicit context: ContextSupport) =\n    (for {\n      userConfig <- apiUtils.getRequestConfig\n      _          <- apiServices.subscribe(sharedCollectionId)(userConfig)\n      collection <- getCollectionBySharedCollectionId(sharedCollectionId)\n      _          <- persistenceServices.updateCollection(collection.copy(sharedCollectionSubscribed = true))\n    } yield ()).resolveLeft(mapLeft)\n\n  override def unsubscribe(sharedCollectionId: String)(implicit context: ContextSupport) =\n    (for {\n      userConfig <- apiUtils.getRequestConfig\n      _          <- apiServices.unsubscribe(sharedCollectionId)(userConfig)\n      collection <- getCollectionBySharedCollectionId(sharedCollectionId)\n      _ <- persistenceServices.updateCollection(\n        collection.copy(sharedCollectionSubscribed = false))\n    } yield ()).resolveLeft(mapLeft)\n\n  override def updateViewSharedCollection(sharedCollectionId: String)(\n      implicit context: ContextSupport): TaskService[Unit] =\n    (for {\n      userConfig <- apiUtils.getRequestConfig\n      _          <- apiServices.updateViewShareCollection(sharedCollectionId)(userConfig)\n    } yield ()).resolveLeft(mapLeft)\n\n  private[this] def getCollectionBySharedCollectionId(sharedCollectionId: String) =\n    persistenceServices\n      .fetchCollectionBySharedCollectionId(sharedCollectionId)\n      .resolveSides(\n        mapRight = {\n          case Some(collection) => Right(collection)\n          case None =>\n            Left(SharedCollectionsException(\"There is no collection with this sharedCollectionId\"))\n        },\n        mapLeft = (e: Throwable) => Left(SharedCollectionsException(e.getMessage, Some(e)))\n      )\n\n  private[this] def mapLeft[T]: (NineCardException) => Either[NineCardException, T] = {\n    case e: ApiServiceConfigurationException =>\n      Left(SharedCollectionsConfigurationException(e.message, Some(e)))\n    case e => Left(SharedCollectionsException(e.message, Some(e)))\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/theme/ThemeExceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.theme\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class ThemeException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\ntrait ImplicitsThemeException {\n  implicit def themeException = (t: Throwable) => ThemeException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/theme/ThemeProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.theme\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.NineCardsTheme\n\ntrait ThemeProcess {\n\n  /**\n   * Gets the teme\n   *\n   * @param themeFile with the name of the file\n   * @return the cards.nine.process.theme.models.NineCardsTheme\n   */\n  def getTheme(themeFile: String)(implicit context: ContextSupport): TaskService[NineCardsTheme]\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/theme/impl/ThemeProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.theme.impl\n\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.commons.utils.{AssetException, FileUtils, ImplicitsAssetException}\nimport cards.nine.models.NineCardsTheme\nimport cards.nine.models.NineCardsThemeImplicits._\nimport cards.nine.process.theme.{ImplicitsThemeException, ThemeException, ThemeProcess}\nimport play.api.libs.json.Json\n\nimport scala.util.{Failure, Success}\n\nclass ThemeProcessImpl\n    extends ThemeProcess\n    with ImplicitsThemeException\n    with ImplicitsAssetException {\n\n  val fileUtils = new FileUtils()\n\n  override def getTheme(themeFile: String)(implicit context: ContextSupport) = {\n\n    def getJsonFromThemeFile = TaskService {\n      CatchAll[AssetException] {\n        fileUtils.readFile(s\"$themeFile.json\") match {\n          case Success(json) => json\n          case Failure(ex)   => throw ex\n        }\n      }\n    }\n\n    def getNineCardsThemeFromJson(json: String) = TaskService {\n      CatchAll[ThemeException](Json.parse(json).as[NineCardsTheme])\n    }\n\n    for {\n      json  <- getJsonFromThemeFile\n      theme <- getNineCardsThemeFromJson(json)\n    } yield theme\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class TrackEventException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsTrackEventException {\n  implicit def trackEventExceptionConverter =\n    (t: Throwable) => TrackEventException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/TrackEventProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent\n\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.{Category, MomentCategory, NineCardsMoment}\n\ntrait TrackEventProcess {\n\n  /* AppDrawerScreen */\n\n  /**\n   * Tracks when the user uses the fast scroller\n   */\n  def usingFastScroller(): TaskService[Unit]\n\n  /**\n   * Tracks when the user goes to contacts\n   */\n  def goToContacts(): TaskService[Unit]\n\n  /**\n   * Tracks when the user goes to apps\n   */\n  def goToApps(): TaskService[Unit]\n\n  /**\n   * Tracks when the user adds an app to a collection\n   *\n   * @param packageName the package's name\n   */\n  def addAppToCollection(packageName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user adds a contact to a collection\n   */\n  def addContactToCollection(): TaskService[Unit]\n\n  /**\n   * Tracks when the user the Google Play search button\n   */\n  def goToGooglePlayButton(): TaskService[Unit]\n\n  /**\n   * Tracks when the user the Google call button\n   */\n  def goToGoogleCallButton(): TaskService[Unit]\n\n  /**\n   * Tracks when the user filters the apps\n   *\n   * @param filterName the filter's name\n   */\n  def goToFiltersByButton(filterName: String): TaskService[Unit]\n\n  /* CollectionDetailScreen */\n\n  /**\n   * Tracks when the user uses the navigation bar\n   */\n  def useNavigationBar(): TaskService[Unit]\n\n  /**\n   * Tracks when the user reorder an app in the collection\n   *\n   * @param newPosition the new app's position\n   */\n  def reorderApplication(newPosition: Int): TaskService[Unit]\n\n  /**\n   * Tracks when the user moves an app to another collection\n   *\n   * @param collectionName the new collection's name\n   */\n  def moveApplications(collectionName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user removes some apps\n   *\n   * @param packageNames the sequence with the removed apps' packageNames\n   */\n  def removeApplications(packageNames: Seq[String]): TaskService[Unit]\n\n  /**\n   * Tracks when the user removes some apps\n   */\n  def closeCollectionByGesture(): TaskService[Unit]\n\n  /**\n   * Tracks when the user adds some shortcut by FAB\n   *\n   * @param shortcutName the shortcut's name\n   */\n  def addShortcutByFab(shortcutName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user adds some shortcut from a receiver\n   *\n   * @param shortcutName the shortcut's name\n   */\n  def addShortcutFromReceiver(shortcutName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user adds a recommended app by FAB\n   *\n   * @param packageName the package's name\n   */\n  def addRecommendationByFab(packageName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user adds a recommended app by FAB\n   */\n  def addContactByFab(): TaskService[Unit]\n\n  /**\n   * Tracks when the user add some apps by the FAB\n   *\n   * @param packageNames the sequence with the added apps' packageNames\n   */\n  def addAppsByFab(packageNames: Seq[String]): TaskService[Unit]\n\n  /**\n   * Tracks when the user removes some apps by the FAB\n   *\n   * @param packageNames the sequence with the removed apps' packageNames\n   */\n  def removeAppsByFab(packageNames: Seq[String]): TaskService[Unit]\n\n  /**\n   * Tracks when the user uses the adds a card menu option\n   */\n  def addCardByMenu(): TaskService[Unit]\n\n  /**\n   * Tracks when the user starts publishing a collection\n   *\n   * @param collectionName the collection's name\n   */\n  def publishCollectionByMenu(collectionName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user shares a just published a collection\n   *\n   * @param sharedCollectionId the sharedCollectionId\n   */\n  def shareCollectionAfterPublishing(sharedCollectionId: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user shares an already published a collection\n   *\n   * @param sharedCollectionId the sharedCollectionId\n   */\n  def shareCollectionByMenu(sharedCollectionId: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user opens an application from collection\n   *\n   * @param packageName package name of app\n   * @param category category of event\n   */\n  def openAppFromCollection(packageName: String, category: Category): TaskService[Unit]\n\n  /**\n   * Tracks when the user adds an app to collection\n   *\n   * @param packageName package name of app\n   * @param category category of event\n   */\n  def addAppToCollection(packageName: String, category: Category): TaskService[Unit]\n\n  /**\n   * Tracks when the user removes an application in collection\n   *\n   * @param packageName package name of app\n   * @param category category of event\n   */\n  def removeFromCollection(packageName: String, category: Category): TaskService[Unit]\n\n  /* HomeScreen */\n\n  /**\n   * Tracks the collectionName when the user opens a collection\n   *\n   * @param collectionName the collection's name\n   */\n  def openCollectionTitle(collectionName: String): TaskService[Unit]\n\n  /**\n   * Tracks the position when the user opens a collection\n   *\n   * @param position the collection's position\n   */\n  def openCollectionOrder(position: Int): TaskService[Unit]\n\n  /**\n   * Tracks when the user deletes a collection\n   *\n   * @param collectionName the collection's name\n   */\n  def deleteCollection(collectionName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user reorders a collection\n   */\n  def reorderCollection(): TaskService[Unit]\n\n  /**\n   * Tracks when the user uses the search by keyboard\n   */\n  def usingSearchByKeyboard(): TaskService[Unit]\n\n  /**\n   * Tracks when the user uses the search by voice\n   */\n  def usingSearchByVoice(): TaskService[Unit]\n\n  /**\n   * Tracks when the user creates a new collection\n   */\n  def createNewCollection(): TaskService[Unit]\n\n  /**\n   * Tracks when the user edits a collection\n   *\n   * @param collectionName the collection's name\n   */\n  def editCollection(collectionName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user opens MyCollections\n   */\n  def openMyCollections(): TaskService[Unit]\n\n  /**\n   * Tracks when the user opens PublicCollections\n   */\n  def openPublicCollections(): TaskService[Unit]\n\n  /**\n   * Tracks when the user creates a new collection from MyCollections\n   *\n   * @param collectionName the collection's name\n   */\n  def createNewCollectionFromMyCollection(collectionName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user creates a new collection from PublicCollections\n   *\n   * @param collectionName the collection's name\n   */\n  def createNewCollectionFromPublicCollection(collectionName: String): TaskService[Unit]\n\n  /**\n   * Tracks the title when the user opens a dock's app\n   *\n   * @param packageName the collection's name\n   */\n  def openDockAppTitle(packageName: String): TaskService[Unit]\n\n  /**\n   * Tracks the order when the user opens a dock's app\n   *\n   * @param position the collection's name\n   */\n  def openDockAppOrder(position: Int): TaskService[Unit]\n\n  /**\n   * Tracks when the user starts publishing a collection\n   */\n  def goToAppDrawer(): TaskService[Unit]\n\n  /**\n   * Tracks when an external app link is received\n   *\n   * @param supported indicates if the link is supported by the app or not\n   */\n  def appLinkReceived(supported: Boolean): TaskService[Unit]\n\n  /**\n   * Tracks when an external shared content is received\n   *\n   * @param supported indicates if the link is supported by the app or not\n   */\n  def sharedContentReceived(supported: Boolean): TaskService[Unit]\n\n  /* LauncherScreen */\n\n  /**\n   * Tracks when the user opens an application from app drawer\n   *\n   * @param packageName package name of app\n   * @param category category of event\n   */\n  def openAppFromAppDrawer(packageName: String, category: Category): TaskService[Unit]\n\n  /* MomentsScreen */\n\n  /**\n   * Tracks when the user opens an app by the icon bar related with the current moment\n   *\n   * @param momentName the moment's name\n   */\n  def openApplicationByMoment(momentName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user goes to edit a moment from the workspace menu\n   *\n   * @param momentName the moment's name\n   */\n  def editMoment(momentName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user goes to change a moment from the workspace menu\n   *\n   * @param momentName the moment's name\n   */\n  def changeMoment(momentName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user goes to add a moment from the workspace menu\n   *\n   * @param momentName the moment's name\n   */\n  def addMoment(momentName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user goes to add a widget from the workspace menu\n   *\n   * @param widgetName the moment's name\n   */\n  def addWidget(widgetName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user unpins the active moment from the top bar\n   */\n  def unpinMoment(): TaskService[Unit]\n\n  /**\n   * Tracks when the user goes to the weather from the top bar\n   */\n  def goToWeather(): TaskService[Unit]\n\n  /**\n   * Tracks when the user sets a Collection for quick access\n   */\n  def quickAccessToCollection(): TaskService[Unit]\n\n  /**\n   * Tracks when the user sets the moment's hour\n   */\n  def setHours(): TaskService[Unit]\n\n  /**\n   * Tracks when the user sets the moment's wifi\n   */\n  def setWifi(): TaskService[Unit]\n\n  /**\n   * Tracks when the user sets the moment's bluetooth\n   */\n  def setBluetooth(): TaskService[Unit]\n\n  /**\n   * Tracks when the user deletes a moment from the moment's menu\n   */\n  def deleteMoment(): TaskService[Unit]\n\n  /* ProfileScreen */\n\n  /**\n   * Tracks when the user enters in logs out\n   */\n  def logout(): TaskService[Unit]\n\n  /**\n   * Tracks when the user enters in the Accounts tab in Profile\n   */\n  def showAccountsContent(): TaskService[Unit]\n\n  /**\n   * Tracks when the user copies a configuration\n   */\n  def copyConfiguration(): TaskService[Unit]\n\n  /**\n   * Tracks when the user synchronizes the current configuration\n   */\n  def synchronizeConfiguration(): TaskService[Unit]\n\n  /**\n   * Tracks when the user synchronizes the configuration name\n   */\n  def changeConfigurationName(): TaskService[Unit]\n\n  /**\n   * Tracks when the user deletes a configuration\n   */\n  def deleteConfiguration(): TaskService[Unit]\n\n  /**\n   * Tracks when the user enters in the Publications tab in Profile\n   */\n  def showPublicationsContent(): TaskService[Unit]\n\n  /**\n   * Tracks when the user adds a collection from the Publications tab\n   *\n   * @param collectionName name of the collection\n   */\n  def addToMyCollectionsFromProfile(collectionName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user shares a collection from the Publications tab\n   *\n   * @param collectionName name of the collection\n   */\n  def shareCollectionFromProfile(collectionName: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user enters in the Subscriptions tab in Profile\n   */\n  def showSubscriptionsContent(): TaskService[Unit]\n\n  /**\n   * Tracks when the user subscribes to a collection in the Subscriptions tab\n   *\n   * @param sharedCollectionId of the collection\n   */\n  def subscribeToCollection(sharedCollectionId: String): TaskService[Unit]\n\n  /**\n   * Tracks when the user unsubscribes from a collection in the Subscriptions tab\n   *\n   * @param sharedCollectionId of the collection\n   */\n  def unsubscribeFromCollection(sharedCollectionId: String): TaskService[Unit]\n\n  /* SliderMenuScreen */\n\n  /**\n   * Tracks when the user goes to Collections by the slider menu\n   */\n  def goToCollectionsByMenu(): TaskService[Unit]\n\n  /**\n   * Tracks when the user goes to Moments by the slider menu\n   */\n  def goToMomentsByMenu(): TaskService[Unit]\n\n  /**\n   * Tracks when the user goes to Profile by the slider menu\n   */\n  def goToProfileByMenu(): TaskService[Unit]\n\n  /**\n   * Tracks when the user goes to Send us feedback by the slider menu\n   */\n  def goToSendUsFeedback(): TaskService[Unit]\n\n  /**\n   * Tracks when the user goes to Help by the slider menu\n   */\n  def goToHelpByMenu(): TaskService[Unit]\n\n  /* WidgetScreen */\n\n  /**\n   * Tracks when the user adds a widget in moment\n   *\n   * @param packageName package name of app\n   * @param className class of the widget\n   * @param moment moment where it's added\n   */\n  def addWidgetToMoment(\n      packageName: String,\n      className: String,\n      moment: MomentCategory): TaskService[Unit]\n\n  /* WizardScreen */\n\n  /**\n   * Tracks when the user choose an account in the Wizard's start screen\n   */\n  def chooseAccount(): TaskService[Unit]\n\n  /**\n   * Tracks when the user choose a configuration in the Wizard's configuration screen\n   */\n  def chooseNewConfiguration(): TaskService[Unit]\n\n  /**\n   * Tracks when the user choose an existing device in the Wizard's configuration screen\n   */\n  def chooseExistingDevice(): TaskService[Unit]\n\n  /**\n   * Tracks when the user choose a moment in the Wizard's moments screen\n   */\n  def chooseMoment(moment: NineCardsMoment): TaskService[Unit]\n\n  /**\n   * Tracks when the user choose sets the wifi for a moment in the Wizard's moments screen\n   */\n  def chooseMomentWifi(moment: NineCardsMoment): TaskService[Unit]\n\n  /**\n   * Tracks when the user choose other moment in the Wizard's other moments screen\n   */\n  def chooseOtherMoment(moment: NineCardsMoment): TaskService[Unit]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/impl/AppDrawerEventProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\nimport cards.nine.process.trackevent.{\n  ImplicitsTrackEventException,\n  TrackEventException,\n  TrackEventProcess\n}\n\ntrait AppDrawerEventProcessImpl extends TrackEventProcess {\n\n  self: TrackEventDependencies with ImplicitsTrackEventException =>\n\n  override def usingFastScroller() = {\n    val event = TrackEvent(\n      screen = AppDrawerScreen,\n      category = FastScrollerCategory,\n      action = UsingFastScrollerAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def goToContacts() = {\n    val event = TrackEvent(\n      screen = AppDrawerScreen,\n      category = GestureActionsCategory,\n      action = GoToContactsAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def goToApps() = {\n    val event = TrackEvent(\n      screen = AppDrawerScreen,\n      category = GestureActionsCategory,\n      action = GoToAppsAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def addAppToCollection(packageName: String) = {\n    val event = TrackEvent(\n      screen = AppDrawerScreen,\n      category = GestureActionsCategory,\n      action = AddAppToCollectionAction,\n      label = Option(packageName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def addContactToCollection() = {\n    val event = TrackEvent(\n      screen = AppDrawerScreen,\n      category = GestureActionsCategory,\n      action = AddContactToCollectionAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def goToGooglePlayButton() = {\n    val event = TrackEvent(\n      screen = AppDrawerScreen,\n      category = SearchButtonsCategory,\n      action = GoToGooglePlayButtonAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def goToGoogleCallButton() = {\n    val event = TrackEvent(\n      screen = AppDrawerScreen,\n      category = SearchButtonsCategory,\n      action = GoToGoogleCallButtonAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def goToFiltersByButton(filterName: String) = {\n    val event = TrackEvent(\n      screen = AppDrawerScreen,\n      category = SearchButtonsCategory,\n      action = GoToFiltersByButtonAction,\n      label = Option(filterName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/impl/CollectionDetailTrackEventProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\nimport cards.nine.process.trackevent.{\n  ImplicitsTrackEventException,\n  TrackEventException,\n  TrackEventProcess\n}\n\ntrait CollectionDetailTrackEventProcessImpl extends TrackEventProcess {\n\n  self: TrackEventDependencies with ImplicitsTrackEventException =>\n\n  override def useNavigationBar() = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = NavigationBarAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def reorderApplication(newPosition: Int) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = ReorderApplicationAction,\n      label = Option(newPosition.toString),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def moveApplications(collectionName: String) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = MoveApplicationsAction,\n      label = Option(collectionName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def removeApplications(packageNames: Seq[String]) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = RemoveApplicationsAction,\n      label = Option(packageNames.mkString(\",\")),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def closeCollectionByGesture() = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = CloseCollectionByGestureAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def addShortcutByFab(shortcutName: String) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = AddShortcutByFabAction,\n      label = Option(shortcutName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def addShortcutFromReceiver(shortcutName: String) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = AddShortcutFromReceiverAction,\n      label = Option(shortcutName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def addRecommendationByFab(packageName: String) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = AddRecommendationByFabAction,\n      label = Option(packageName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def addContactByFab() = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = AddContactByFabAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def addAppsByFab(packageNames: Seq[String]) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = AddAppsByFabAction,\n      label = Option(packageNames.mkString(\",\")),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def removeAppsByFab(packageNames: Seq[String]) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = RemoveAppsByFabAction,\n      label = Option(packageNames.mkString(\",\")),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def addCardByMenu() = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = AddCardByMenuAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def publishCollectionByMenu(collectionName: String) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = PublishCollectionByMenuAction,\n      label = Option(collectionName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def shareCollectionAfterPublishing(sharedCollectionId: String) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = ShareCollectionAfterPublishingAction,\n      label = Option(sharedCollectionId),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def shareCollectionByMenu(sharedCollectionId: String) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = GestureActionsCategory,\n      action = ShareCollectionByMenuAction,\n      label = Option(sharedCollectionId),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def openAppFromCollection(packageName: String, category: Category) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = category,\n      action = OpenCardAction,\n      label = Option(packageName),\n      value = Option(OpenAppFromCollectionValue))\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def addAppToCollection(packageName: String, category: Category) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = category,\n      action = AddedToCollectionAction,\n      label = Option(packageName),\n      value = Option(AddedToCollectionValue))\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def removeFromCollection(packageName: String, category: Category) = {\n    val event = TrackEvent(\n      screen = CollectionDetailScreen,\n      category = category,\n      action = RemovedFromCollectionAction,\n      label = Option(packageName),\n      value = Option(RemovedFromCollectionValue))\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/impl/HomeTrackEventProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\nimport cards.nine.process.trackevent.{\n  ImplicitsTrackEventException,\n  TrackEventException,\n  TrackEventProcess\n}\n\ntrait HomeTrackEventProcessImpl extends TrackEventProcess {\n\n  self: TrackEventDependencies with ImplicitsTrackEventException =>\n\n  override def openCollectionTitle(collectionName: String) = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceCategory,\n      action = OpenCollectionTitleAction,\n      label = Option(collectionName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def openCollectionOrder(position: Int) = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceCategory,\n      action = OpenCollectionOrderAction,\n      label = Option(position.toString),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def deleteCollection(collectionName: String) = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceDragAndDropCategory,\n      action = DeleteCollectionAction,\n      label = Option(collectionName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def reorderCollection() = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceCategory,\n      action = ReorderCollectionAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def usingSearchByKeyboard() = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = SearchButtonsCategory,\n      action = UsingSearchByKeyboardAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def usingSearchByVoice() = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = SearchButtonsCategory,\n      action = UsingSearchByVoiceAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def createNewCollection() = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceActionsCategory,\n      action = CreateNewCollectionAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def editCollection(collectionName: String) = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceActionsCategory,\n      action = EditCollectionAction,\n      label = Option(collectionName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def openMyCollections() = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceActionsCategory,\n      action = OpenMyCollectionsAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def openPublicCollections() = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceActionsCategory,\n      action = OpenPublicCollectionsAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def createNewCollectionFromMyCollection(collectionName: String) = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceActionsCategory,\n      action = CreateNewCollectionFromMyCollectionAction,\n      label = Option(collectionName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def createNewCollectionFromPublicCollection(collectionName: String) = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceActionsCategory,\n      action = CreateNewCollectionFromPublicCollectionAction,\n      label = Option(collectionName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def openDockAppTitle(packageName: String) = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceBottomActionsCategory,\n      action = OpenDockAppTitleAction,\n      label = Option(packageName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def openDockAppOrder(position: Int) = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceBottomActionsCategory,\n      action = OpenDockAppOrderAction,\n      label = Option(position.toString),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def goToAppDrawer() = {\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceBottomActionsCategory,\n      action = GoToAppDrawerAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def appLinkReceived(supported: Boolean) = {\n    val supportedLabel    = \"Supported\"\n    val notSupportedLabel = \"Not Supported\"\n    val label             = if (supported) supportedLabel else notSupportedLabel\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceLinkReceived,\n      action = AppLinkReceivedAction,\n      label = Option(label),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def sharedContentReceived(supported: Boolean) = {\n    val supportedLabel    = \"Supported\"\n    val notSupportedLabel = \"Not Supported\"\n    val label             = if (supported) supportedLabel else notSupportedLabel\n    val event = TrackEvent(\n      screen = HomeScreen,\n      category = WorkSpaceLinkReceived,\n      action = SharedContentReceivedAction,\n      label = Option(label),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/impl/LauncherTrackEventProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\nimport cards.nine.process.trackevent.{\n  ImplicitsTrackEventException,\n  TrackEventException,\n  TrackEventProcess\n}\nimport cats.implicits._\nimport monix.eval.Task\n\ntrait LauncherTrackEventProcessImpl extends TrackEventProcess {\n\n  self: TrackEventDependencies with ImplicitsTrackEventException =>\n\n  override def openAppFromAppDrawer(packageName: String, category: Category) = {\n    val event = TrackEvent(\n      screen = LauncherScreen,\n      category = category,\n      action = OpenAction,\n      label = Option(packageName),\n      value = Option(OpenAppFromAppDrawerValue))\n\n    def eventForGames(category: Category): TaskService[Unit] =\n      category match {\n        case AppCategory(nineCardCategory) if nineCardCategory.isGameCategory =>\n          trackServices\n            .trackEvent(event.copy(category = AppCategory(Game)))\n            .resolve[TrackEventException]\n        case _ => TaskService(Task(Right(())))\n      }\n\n    (trackServices.trackEvent(event) *> eventForGames(category)).resolve[TrackEventException]\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/impl/MomentsTrackEventProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\nimport cards.nine.process.trackevent.{\n  ImplicitsTrackEventException,\n  TrackEventException,\n  TrackEventProcess\n}\n\ntrait MomentsTrackEventProcessImpl extends TrackEventProcess {\n\n  self: TrackEventDependencies with ImplicitsTrackEventException =>\n\n  override def openApplicationByMoment(momentName: String) = {\n    val event = TrackEvent(\n      screen = MomentsScreen,\n      category = IconBarCategory,\n      action = OpenApplicationByMomentAction,\n      label = Option(momentName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def editMoment(momentName: String) = {\n    val event = TrackEvent(\n      screen = MomentsScreen,\n      category = WorkSpaceActionsCategory,\n      action = EditMomentAction,\n      label = Option(momentName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def changeMoment(momentName: String) = {\n    val event = TrackEvent(\n      screen = MomentsScreen,\n      category = WorkSpaceActionsCategory,\n      action = ChangeMomentAction,\n      label = Option(momentName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def addMoment(momentName: String) = {\n    val event = TrackEvent(\n      screen = MomentsScreen,\n      category = WorkSpaceActionsCategory,\n      action = AddMomentAction,\n      label = Option(momentName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def addWidget(widgetName: String) = {\n    val event = TrackEvent(\n      screen = MomentsScreen,\n      category = WorkSpaceActionsCategory,\n      action = AddWidgetAction,\n      label = Option(widgetName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def unpinMoment() = {\n    val event = TrackEvent(\n      screen = MomentsScreen,\n      category = TopBarCategory,\n      action = UnpinMomentAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def goToWeather() = {\n    val event = TrackEvent(\n      screen = MomentsScreen,\n      category = TopBarCategory,\n      action = GoToWeatherAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def quickAccessToCollection() = {\n    val event = TrackEvent(\n      screen = MomentsScreen,\n      category = EditMomentCategory,\n      action = QuickAccessToCollectionAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def setHours() = {\n    val event = TrackEvent(\n      screen = MomentsScreen,\n      category = EditMomentCategory,\n      action = SetHoursAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def setWifi() = {\n    val event = TrackEvent(\n      screen = MomentsScreen,\n      category = EditMomentCategory,\n      action = SetWifiAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def setBluetooth() = {\n    val event = TrackEvent(\n      screen = MomentsScreen,\n      category = EditMomentCategory,\n      action = SetBluetoothAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def deleteMoment() = {\n    val event = TrackEvent(\n      screen = MomentsScreen,\n      category = MomentsMenuCategory,\n      action = DeleteMomentAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/impl/ProfileTrackEventProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\nimport cards.nine.process.trackevent.{\n  ImplicitsTrackEventException,\n  TrackEventException,\n  TrackEventProcess\n}\n\ntrait ProfileTrackEventProcessImpl extends TrackEventProcess {\n\n  self: TrackEventDependencies with ImplicitsTrackEventException =>\n\n  override def logout() = {\n    val event = TrackEvent(\n      screen = ProfileScreen,\n      category = AccountCategory,\n      action = LogoutAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def showAccountsContent() = {\n    val event = TrackEvent(\n      screen = ProfileScreen,\n      category = AccountCategory,\n      action = ShowAccountsContentAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def copyConfiguration() = {\n    val event = TrackEvent(\n      screen = ProfileScreen,\n      category = AccountCategory,\n      action = CopyConfigurationAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def synchronizeConfiguration() = {\n    val event = TrackEvent(\n      screen = ProfileScreen,\n      category = AccountCategory,\n      action = SynchronizeConfigurationAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def changeConfigurationName() = {\n    val event = TrackEvent(\n      screen = ProfileScreen,\n      category = AccountCategory,\n      action = ChangeConfigurationNameAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def deleteConfiguration() = {\n    val event = TrackEvent(\n      screen = ProfileScreen,\n      category = AccountCategory,\n      action = DeleteConfigurationAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def showPublicationsContent() = {\n    val event = TrackEvent(\n      screen = ProfileScreen,\n      category = PublicationCategory,\n      action = ShowPublicationsContentAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def addToMyCollectionsFromProfile(collectionName: String) = {\n    val event = TrackEvent(\n      screen = ProfileScreen,\n      category = PublicationCategory,\n      action = AddToMyCollectionsFromProfileAction,\n      label = Option(collectionName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def shareCollectionFromProfile(collectionName: String) = {\n    val event = TrackEvent(\n      screen = ProfileScreen,\n      category = PublicationCategory,\n      action = ShareCollectionFromProfileAction,\n      label = Option(collectionName),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def showSubscriptionsContent() = {\n    val event = TrackEvent(\n      screen = ProfileScreen,\n      category = SubscriptionCategory,\n      action = ShowSubscriptionsContentAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def subscribeToCollection(sharedCollectionId: String) = {\n    val event = TrackEvent(\n      screen = ProfileScreen,\n      category = SubscriptionCategory,\n      action = SubscribeToCollectionAction,\n      label = Option(sharedCollectionId),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def unsubscribeFromCollection(sharedCollectionId: String) = {\n    val event = TrackEvent(\n      screen = ProfileScreen,\n      category = SubscriptionCategory,\n      action = UnsubscribeFromCollectionAction,\n      label = Option(sharedCollectionId),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/impl/SliderMenuTrackEventProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\nimport cards.nine.process.trackevent.{\n  ImplicitsTrackEventException,\n  TrackEventException,\n  TrackEventProcess\n}\n\ntrait SliderMenuTrackEventProcessImpl extends TrackEventProcess {\n\n  self: TrackEventDependencies with ImplicitsTrackEventException =>\n\n  override def goToCollectionsByMenu() = {\n    val event = TrackEvent(\n      screen = SliderMenuScreen,\n      category = SliderOptionCategory,\n      action = GoToCollectionsByMenuAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def goToMomentsByMenu() = {\n    val event = TrackEvent(\n      screen = SliderMenuScreen,\n      category = SliderOptionCategory,\n      action = GoToMomentsByMenuAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def goToProfileByMenu() = {\n    val event = TrackEvent(\n      screen = SliderMenuScreen,\n      category = SliderOptionCategory,\n      action = GoToProfileByMenuAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def goToSendUsFeedback() = {\n    val event = TrackEvent(\n      screen = SliderMenuScreen,\n      category = SliderOptionCategory,\n      action = GoToSendUsFeedbackAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def goToHelpByMenu() = {\n    val event = TrackEvent(\n      screen = SliderMenuScreen,\n      category = SliderOptionCategory,\n      action = GoToHelpAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/impl/TrackEventDependencies.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.services.track.TrackServices\n\ntrait TrackEventDependencies {\n  val trackServices: TrackServices\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/impl/TrackEventProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.process.trackevent._\nimport cards.nine.services.track.TrackServices\n\nclass TrackEventProcessImpl(val trackServices: TrackServices)\n    extends TrackEventProcess\n    with TrackEventDependencies\n    with AppDrawerEventProcessImpl\n    with CollectionDetailTrackEventProcessImpl\n    with HomeTrackEventProcessImpl\n    with LauncherTrackEventProcessImpl\n    with MomentsTrackEventProcessImpl\n    with ProfileTrackEventProcessImpl\n    with SliderMenuTrackEventProcessImpl\n    with WidgetTrackEventProcessImpl\n    with WizardTrackEventProcessImpl\n    with ImplicitsTrackEventException\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/impl/WidgetTrackEventProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types.{\n  AddedWidgetToMomentAction,\n  AddedWidgetToMomentValue,\n  MomentCategory,\n  WidgetScreen\n}\nimport cards.nine.process.trackevent.{\n  ImplicitsTrackEventException,\n  TrackEventException,\n  TrackEventProcess\n}\n\ntrait WidgetTrackEventProcessImpl extends TrackEventProcess {\n\n  self: TrackEventDependencies with ImplicitsTrackEventException =>\n\n  def addWidgetToMoment(packageName: String, className: String, moment: MomentCategory) = {\n    val widgetLabel = s\"$packageName:$className\"\n    val event = TrackEvent(\n      screen = WidgetScreen,\n      category = moment,\n      action = AddedWidgetToMomentAction,\n      label = Option(widgetLabel),\n      value = Option(AddedWidgetToMomentValue))\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/trackevent/impl/WizardTrackEventProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types._\nimport cards.nine.process.trackevent.{\n  ImplicitsTrackEventException,\n  TrackEventException,\n  TrackEventProcess\n}\n\ntrait WizardTrackEventProcessImpl extends TrackEventProcess {\n\n  self: TrackEventDependencies with ImplicitsTrackEventException =>\n\n  override def chooseAccount() = {\n    val event = TrackEvent(\n      screen = WizardScreen,\n      category = WizardStartCategory,\n      action = ChooseAccountAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def chooseNewConfiguration() = {\n    val event = TrackEvent(\n      screen = WizardScreen,\n      category = WizardConfigurationCategory,\n      action = ChooseNewConfigurationAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def chooseExistingDevice() = {\n    val event = TrackEvent(\n      screen = WizardScreen,\n      category = WizardConfigurationCategory,\n      action = ChooseExistingDeviceAction,\n      label = None,\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def chooseMoment(moment: NineCardsMoment) = {\n    val event = TrackEvent(\n      screen = WizardScreen,\n      category = WizardMomentsWifiCategory,\n      action = ChooseMomentAction,\n      label = Option(moment.name),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def chooseMomentWifi(moment: NineCardsMoment) = {\n    val event = TrackEvent(\n      screen = WizardScreen,\n      category = WizardMomentsWifiCategory,\n      action = ChooseMomentWifiAction,\n      label = Option(moment.name),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n  override def chooseOtherMoment(moment: NineCardsMoment) = {\n    val event = TrackEvent(\n      screen = WizardScreen,\n      category = WizardOtherMomentsCategory,\n      action = ChooseOtherMomentAction,\n      label = Option(moment.name),\n      value = None)\n    trackServices.trackEvent(event).resolve[TrackEventException]\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/user/UserExceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.user\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class UserException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsUserException {\n  implicit def userException = (t: Throwable) => UserException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/user/UserProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.user\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.User\n\ntrait UserProcess {\n\n  def signIn(email: String, androidMarketToken: String, emailTokenId: String)(\n      implicit context: ContextSupport): TaskService[Unit]\n\n  def register(implicit context: ContextSupport): TaskService[Unit]\n\n  def unregister(implicit context: ContextSupport): TaskService[Unit]\n\n  def getUser(implicit context: ContextSupport): TaskService[User]\n\n  def updateUserDevice(\n      deviceName: String,\n      deviceCloudId: String,\n      deviceToken: Option[String] = None)(implicit context: ContextSupport): TaskService[Unit]\n\n  def updateDeviceToken(deviceToken: String)(implicit context: ContextSupport): TaskService[Unit]\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/user/impl/UserProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.user.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{RequestConfig, User, UserData, UserProfile}\nimport cards.nine.process.user._\nimport cards.nine.services.api.ApiServices\nimport cards.nine.services.persistence._\nimport cats.syntax.either._\nimport monix.eval.Task\n\nclass UserProcessImpl(apiServices: ApiServices, persistenceServices: PersistenceServices)\n    extends UserProcess\n    with ImplicitsUserException {\n\n  private[this] val noActiveUserErrorMessage = \"No active user\"\n\n  val emptyUser = UserData(None, None, None, None, None, None, None, UserProfile(None, None, None))\n\n  override def signIn(email: String, androidMarketToken: String, emailTokenId: String)(\n      implicit context: ContextSupport) = {\n    withActiveUser { id =>\n      (for {\n        androidId     <- persistenceServices.getAndroidId\n        loginResponse <- apiServices.login(email, androidId, emailTokenId)\n        userDB        <- findUserById(id)\n        updateUser = userDB.copy(\n          id = id,\n          email = Some(email),\n          apiKey = Option(loginResponse.apiKey),\n          sessionToken = Option(loginResponse.sessionToken),\n          marketToken = Option(androidMarketToken))\n        _ <- persistenceServices.updateUser(updateUser)\n      } yield ()).resolve[UserException]\n    }\n  }\n\n  override def register(implicit context: ContextSupport) = {\n\n    def checkOrAddUser(id: Int)(implicit context: ContextSupport): TaskService[User] =\n      (for {\n        maybeUser <- persistenceServices.findUserById(id)\n        user <- maybeUser map (user => TaskService(Task(Either.right(user)))) getOrElse {\n          persistenceServices.addUser(emptyUser)\n        }\n      } yield user).resolve[UserException]\n\n    def getFirstOrAddUser(implicit context: ContextSupport): TaskService[User] =\n      (for {\n        maybeUsers <- persistenceServices.fetchUsers\n        user <- maybeUsers.headOption map (user =>\n                                             TaskService(Task(Either.right(user)))) getOrElse {\n          persistenceServices.addUser(emptyUser)\n        }\n      } yield user).resolve[UserException]\n\n    context.getActiveUserId map { id =>\n      (for {\n        user <- checkOrAddUser(id)\n        _ = if (id != user.id) context.setActiveUserId(user.id)\n      } yield ()).resolve[UserException]\n    } getOrElse {\n      (for {\n        user <- getFirstOrAddUser\n        _ = context.setActiveUserId(user.id)\n      } yield ()).resolve[UserException]\n    }\n  }\n\n  override def unregister(implicit context: ContextSupport) =\n    withActiveUser { id =>\n      val update =\n        User(id, None, None, None, None, None, None, None, UserProfile(None, None, None))\n      (for {\n        user <- findUserById(id)\n        _    <- persistenceServices.updateUser(update)\n        _    <- syncInstallation(user.apiKey, user.sessionToken, None)\n      } yield ()).resolve[UserException]\n    }\n\n  override def getUser(implicit context: ContextSupport) =\n    withActiveUser(findUserById(_).resolve[UserException])\n\n  override def updateUserDevice(\n      deviceName: String,\n      deviceCloudId: String,\n      deviceToken: Option[String] = None)(implicit context: ContextSupport) =\n    withActiveUser { userId =>\n      (for {\n        user <- findUserById(userId)\n        newUser = user.copy(\n          deviceName = Option(deviceName),\n          deviceCloudId = Option(deviceCloudId),\n          deviceToken = deviceToken orElse user.deviceToken)\n        _ <- persistenceServices.updateUser(user = newUser)\n        _ <- syncInstallation(user.apiKey, user.sessionToken, newUser.deviceToken)\n      } yield ()).resolve[UserException]\n    }\n\n  override def updateDeviceToken(deviceToken: String)(implicit context: ContextSupport) =\n    withActiveUser { userId =>\n      (for {\n        user <- findUserById(userId)\n        _ <- persistenceServices.updateUser(\n          user.copy(id = userId, deviceToken = Option(deviceToken)))\n        _ <- syncInstallation(user.apiKey, user.sessionToken, Option(deviceToken))\n      } yield ()).resolve[UserException]\n    }\n\n  private[this] def withActiveUser[T](f: Int => TaskService[T])(implicit context: ContextSupport) =\n    context.getActiveUserId map f getOrElse {\n      TaskService(Task(Either.left(UserException(noActiveUserErrorMessage))))\n    }\n\n  private[this] def syncInstallation(\n      maybeApiKey: Option[String],\n      maybeSessionToken: Option[String],\n      deviceToken: Option[String])(implicit context: ContextSupport): TaskService[Unit] =\n    (maybeApiKey, maybeSessionToken) match {\n      case (Some(apiKey), Some(sessionToken)) if deviceToken.nonEmpty =>\n        (for {\n          androidId <- persistenceServices.getAndroidId\n          _ <- apiServices.updateInstallation(deviceToken)(\n            RequestConfig(apiKey, sessionToken, androidId))\n        } yield ()).resolve[UserException]\n      case _ => TaskService.right(0)\n    }\n\n  private[this] def findUserById(id: Int): TaskService[User] =\n    persistenceServices.findUserById(id).resolveOption(s\"Can't find the user with id $id\")\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/userv1/UserV1Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.userv1\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class UserV1Exception(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class UserV1ConfigurationException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsUserV1Exception {\n  implicit def userConfigException = (t: Throwable) => UserV1Exception(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/userv1/UserV1Process.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.userv1\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.UserV1\n\ntrait UserV1Process {\n  def getUserInfo(deviceName: String, oauthScopes: Seq[String])(\n      implicit context: ContextSupport): TaskService[UserV1]\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/userv1/impl/UserV1ProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.userv1.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models._\nimport cards.nine.process.userv1._\nimport cards.nine.services.api._\nimport cards.nine.services.persistence.PersistenceServices\nimport cats.syntax.either._\nimport monix.eval.Task\n\nclass UserV1ProcessImpl(apiServices: ApiServices, persistenceServices: PersistenceServices)\n    extends UserV1Process\n    with ImplicitsUserV1Exception {\n\n  private[this] val noActiveUserErrorMsg = \"No active user\"\n  private[this] val marketTokenErrorMsg  = \"Market token not available\"\n  private[this] val userNotLoggedMsg     = \"Can't authenticate user against backend V1\"\n\n  override def getUserInfo(deviceName: String, oauthScopes: Seq[String])(\n      implicit context: ContextSupport) = {\n\n    def loginV1(user: User, androidId: String): TaskService[LoginResponseV1] =\n      (user.email, user.marketToken) match {\n        case (Some(email), Some(marketToken)) =>\n          val device = Device(\n            name = deviceName,\n            deviceId = androidId,\n            secretToken = marketToken,\n            permissions = oauthScopes)\n          apiServices.loginV1(email, device)\n        case _ =>\n          TaskService(Task(Either.left(UserV1Exception(marketTokenErrorMsg))))\n      }\n\n    def requestConfig(\n        androidId: String,\n        maybeSessionToken: Option[String],\n        marketToken: Option[String]): TaskService[UserV1] =\n      maybeSessionToken match {\n        case Some(sessionToken) =>\n          apiServices.getUserConfigV1()(RequestConfigV1(androidId, sessionToken, marketToken))\n        case _ =>\n          TaskService(Task(Either.left(UserV1Exception(userNotLoggedMsg))))\n      }\n\n    def loadUserConfig(userId: Int): TaskService[UserV1] =\n      (for {\n        user <- persistenceServices\n          .findUserById(userId)\n          .resolveOption(s\"Can't find the user with id $userId\")\n        androidId     <- persistenceServices.getAndroidId\n        loginResponse <- loginV1(user, androidId)\n        userConfig    <- requestConfig(androidId, loginResponse.sessionToken, user.marketToken)\n      } yield userConfig) resolveLeft {\n        case e: ApiServiceV1ConfigurationException =>\n          Left(UserV1ConfigurationException(e.getMessage, Some(e)))\n        case e => Left(userConfigException(e))\n      }\n\n    context.getActiveUserId match {\n      case Some(id) => loadUserConfig(id)\n      case None     => TaskService(Task(Either.left(UserV1Exception(noActiveUserErrorMsg))))\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/utils/ApiUtils.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.utils\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{RequestConfig, User}\nimport cards.nine.services.api.{ApiServiceException, ImplicitsApiServiceExceptions}\nimport cards.nine.services.persistence.PersistenceServices\nimport cats.syntax.either._\nimport monix.eval.Task\n\nclass ApiUtils(persistenceServices: PersistenceServices) extends ImplicitsApiServiceExceptions {\n\n  def getRequestConfig(implicit context: ContextSupport): TaskService[RequestConfig] = {\n\n    def loadUser(userId: Int): TaskService[RequestConfig] =\n      (for {\n        user <- persistenceServices\n          .findUserById(userId)\n          .resolveOption(s\"Can't find the user with id $userId\")\n        keys <- loadTokens(user)\n        (apiKey, sessionToken) = keys\n        androidId <- persistenceServices.getAndroidId\n      } yield RequestConfig(apiKey, sessionToken, androidId, user.marketToken))\n        .resolve[ApiServiceException]\n\n    def loadTokens(user: User): TaskService[(String, String)] =\n      (user.apiKey, user.sessionToken) match {\n        case (Some(apiKey), Some(sessionToken)) =>\n          TaskService(Task(Either.right(apiKey, sessionToken)))\n        case _ =>\n          TaskService(Task(Either.left(ApiServiceException(\"Session token doesn't exists\"))))\n      }\n\n    context.getActiveUserId match {\n      case Some(id) => loadUser(id)\n      case None     => TaskService(Task(Either.left(ApiServiceException(\"Missing user id\"))))\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/widget/AppWidgetException.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.widget\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class AppWidgetException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsWidgetException {\n  implicit def widgetException = (t: Throwable) => AppWidgetException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/widget/WidgetProcess.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.widget\n\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.{Widget, WidgetData}\n\ntrait WidgetProcess {\n\n  /**\n   * Gets the existing widgets\n   *\n   * @return the Seq[cards.nine.model.Widget] of existing widgets\n   * @throws AppWidgetException if there was an error getting the existing widgets\n   */\n  def getWidgets: TaskService[Seq[Widget]]\n\n  /**\n   * Gets a widget by a given Id\n   *\n   * @param widgetId the Id of the Widget\n   * @return the Option[cards.nine.model.Widget] corresponding with the Id\n   * @throws AppWidgetException if there was an error getting the widget\n   */\n  def getWidgetById(widgetId: Int): TaskService[Option[Widget]]\n\n  /**\n   * Gets a widget by a given appWidgetId\n   *\n   * @param appWidgetId the appWidgetId of the Widget\n   * @return the Option[cards.nine.model.Widget] corresponding with the appWidgetId\n   * @throws AppWidgetException if there was an error getting the widget\n   */\n  def getWidgetByAppWidgetId(appWidgetId: Int): TaskService[Option[Widget]]\n\n  /**\n   * Gets the existing widgets related with a given moment\n   *\n   * @param momentId id of thw Moment\n   * @return the Seq[cards.nine.model.Widget] of existing widgets related with the moment\n   * @throws AppWidgetException if there was an error getting the existing widgets\n   */\n  def getWidgetsByMoment(momentId: Int): TaskService[Seq[Widget]]\n\n  /**\n   * Adds a new widget\n   *\n   * @param addWidgetRequest includes the necessary data to create a new widget\n   * @return the new [cards.nine.model.Widget] added\n   * @throws AppWidgetException if there was an error adding the new widget\n   */\n  def addWidget(addWidgetRequest: WidgetData): TaskService[Widget]\n\n  /**\n   * Adds a sequence of new widgets\n   *\n   * @param request a sequence including the necessary data to create a new widget\n   * @return the Seq[cards.nine.model.Widget] of new widgets added\n   * @throws AppWidgetException if there was an error adding the new widget\n   */\n  def addWidgets(request: Seq[WidgetData]): TaskService[Seq[Widget]]\n\n  /**\n   * Update a list of widgets\n   *\n   * @param widgets list of widgets\n   * @return the new [cards.nine.model.Widget]\n   * @throws AppWidgetException if there was an error finding the widget or moving it\n   */\n  def updateWidgets(widgets: Seq[Widget]): TaskService[Unit]\n\n  /**\n   * Moves an existing widget in the workspace\n   *\n   * @param widgetId the Id of the Widget\n   * @param displaceX includes the new startX and startY coordenates\n   * @return the [cards.nine.model.Widget] with the new position\n   * @throws AppWidgetException if there was an error finding the widget or moving it\n   */\n  @deprecated\n  def moveWidget(widgetId: Int, displaceX: Int, displaceY: Int): TaskService[Widget]\n\n  /**\n   * Resizes an existing widget in the workspace\n   *\n   * @param widgetId the Id of the Widget\n   * @param increaseX includes the new spanX and spanY coordenates\n   * @return the [cards.nine.model.Widget] with the new position\n   * @throws AppWidgetException if there was an error finding the widget or resizing it\n   */\n  @deprecated\n  def resizeWidget(widgetId: Int, increaseX: Int, increaseY: Int): TaskService[Widget]\n\n  /**\n   * Update app widget id of Android SDK in database\n   *\n   * @param widgetId the Id of the Widget\n   * @param appWidgetId app widget id in Android SDK\n   * @return the [cards.nine.model.Widget] with the new position\n   * @throws AppWidgetException if there was an error finding the widget or resizing it\n   */\n  def updateAppWidgetId(widgetId: Int, appWidgetId: Int): TaskService[Widget]\n\n  /**\n   * Delete all widgets in database\n   *\n   * @throws AppWidgetException if exist some problem deleting the widgets\n   */\n  def deleteAllWidgets(): TaskService[Unit]\n\n  /**\n   * Deletes a widget\n   *\n   * @param widgetId the Id of the Widget\n   * @throws AppWidgetException if there was an error finding the widget or resizing it\n   */\n  def deleteWidget(widgetId: Int): TaskService[Unit]\n\n  /**\n   * Delete all widgets in database with a given momentId\n   *\n   * @param momentId the Id of the moment associated with the widgets\n   * @throws AppWidgetException if exist some problem deleting the widgets\n   */\n  def deleteWidgetsByMoment(momentId: Int): TaskService[Unit]\n\n}\n"
  },
  {
    "path": "modules/process/src/main/scala/cards/nine/process/widget/impl/WidgetProcessImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.widget.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{Widget, WidgetData}\nimport cards.nine.process.widget._\nimport cards.nine.services.persistence._\n\nclass WidgetProcessImpl(val persistenceServices: PersistenceServices)\n    extends WidgetProcess\n    with ImplicitsWidgetException {\n\n  override def getWidgets = persistenceServices.fetchWidgets.resolve[AppWidgetException]\n\n  override def getWidgetById(widgetId: Int) =\n    findWidgetById(widgetId).resolve[AppWidgetException]\n\n  override def getWidgetByAppWidgetId(appWidgetId: Int) =\n    persistenceServices.fetchWidgetByAppWidgetId(appWidgetId).resolve[AppWidgetException]\n\n  override def getWidgetsByMoment(momentId: Int) =\n    persistenceServices.fetchWidgetsByMoment(momentId).resolve[AppWidgetException]\n\n  override def addWidget(addWidgetRequest: WidgetData) =\n    persistenceServices.addWidget(addWidgetRequest).resolve[AppWidgetException]\n\n  override def addWidgets(request: Seq[WidgetData]) =\n    persistenceServices.addWidgets(request).resolve[AppWidgetException]\n\n  override def updateWidgets(widgets: Seq[Widget]) =\n    (for {\n      _ <- persistenceServices.updateWidgets(widgets)\n    } yield ()).resolve[AppWidgetException]\n\n  override def moveWidget(widgetId: Int, displaceX: Int, displaceY: Int) = {\n\n    def toUpdatedWidget(widget: Widget, displaceX: Int, displaceY: Int): Widget =\n      widget.copy(\n        area = widget.area\n          .copy(startX = widget.area.startX + displaceX, startY = widget.area.startY + displaceY))\n\n    (for {\n      widget <- fetchWidgetById(widgetId)\n      updatedWidget = toUpdatedWidget(widget, displaceX, displaceY)\n      _ <- updateWidgets(Seq(updatedWidget))\n    } yield updatedWidget).resolve[AppWidgetException]\n\n  }\n\n  override def resizeWidget(widgetId: Int, increaseX: Int, increaseY: Int) = {\n\n    def toUpdatedWidget(widget: Widget, increaseX: Int, increaseY: Int): Widget =\n      widget.copy(\n        area = widget.area\n          .copy(spanX = widget.area.spanX + increaseX, spanY = widget.area.spanY + increaseY))\n\n    (for {\n      widget <- fetchWidgetById(widgetId)\n      updatedWidget = toUpdatedWidget(widget, increaseX, increaseY)\n      _ <- updateWidgets(Seq(updatedWidget))\n    } yield updatedWidget).resolve[AppWidgetException]\n\n  }\n\n  override def updateAppWidgetId(widgetId: Int, appWidgetId: Int) = {\n\n    def toUpdatedWidget(widget: Widget, appWidgetId: Int): Widget =\n      widget.copy(appWidgetId = Option(appWidgetId))\n\n    (for {\n      widget <- fetchWidgetById(widgetId)\n      updatedWidget = toUpdatedWidget(widget, appWidgetId)\n      _ <- updateWidgets(Seq(updatedWidget))\n    } yield updatedWidget).resolve[AppWidgetException]\n  }\n\n  override def deleteAllWidgets() =\n    (for {\n      _ <- persistenceServices.deleteAllWidgets()\n    } yield ()).resolve[AppWidgetException]\n\n  override def deleteWidget(widgetId: Int) =\n    (for {\n      widget <- fetchWidgetById(widgetId)\n      _      <- persistenceServices.deleteWidget(widget)\n    } yield ()).resolve[AppWidgetException]\n\n  override def deleteWidgetsByMoment(momentId: Int) =\n    (for {\n      _ <- persistenceServices.deleteWidgetsByMoment(momentId: Int)\n    } yield ()).resolve[AppWidgetException]\n\n  private[this] def findWidgetById(widgetId: Int) =\n    persistenceServices.findWidgetById(widgetId).resolve[AppWidgetException]\n\n  private[this] def fetchWidgetById(widgetId: Int) =\n    findWidgetById(widgetId).resolveOption(s\"Can't find widget with id $widgetId\")\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/accounts/impl/UserAccountsProcessImplData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.accounts.impl\n\ntrait UserAccountsProcessImplData {\n\n  val accountName1 = \"name1\"\n\n  val accountName2 = \"name2\"\n\n  val accountType = \"com.google\"\n\n  val androidAccount1 = new android.accounts.Account(accountName1, accountType)\n\n  val androidAccount2 = new android.accounts.Account(accountName2, accountType)\n\n  val scope = \"fake-scope\"\n\n  val authToken = \"fake-auth-token\"\n\n  val permissionCode = 100\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/accounts/impl/UserAccountsProcessImplSpecification.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.accounts.impl\n\nimport android.accounts._\nimport android.app.Activity\nimport android.os.Bundle\nimport cards.nine.commons._\nimport cards.nine.commons.contexts.ActivityContextSupport\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.models.types._\nimport cards.nine.process.accounts._\nimport cards.nine.services.permissions.PermissionsServices\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait UserAccountsProcessImplSpecification\n    extends TaskServiceSpecification\n    with Mockito\n    with UserAccountsProcessImplData {\n\n  trait UserAccountsProcessImplScope extends Scope {\n\n    val contextSupport = mock[ActivityContextSupport]\n\n    val activity = mock[Activity]\n\n    val accountManagerFuture = mock[AccountManagerFuture[Bundle]]\n\n    val mockAccountManager = mock[AccountManager]\n\n    contextSupport.getAccountManager returns mockAccountManager\n\n    val bundle = mock[Bundle]\n\n    val securityException = new SecurityException(\"\")\n\n    val runtimeException = new RuntimeException(\"\")\n\n    val operationCancelledException = new OperationCanceledException(\"\")\n\n    val permissionsServices = mock[PermissionsServices]\n\n    val accountsProcess = new UserAccountsProcessImpl(permissionsServices)\n\n  }\n\n}\n\nclass UserAccountsProcessImplSpec extends UserAccountsProcessImplSpecification {\n\n  \"getGoogleAccounts\" should {\n\n    \"return the accounts sequence and call with the right account type\" in\n      new UserAccountsProcessImplScope {\n\n        mockAccountManager.getAccountsByType(any) returns Array(androidAccount1, androidAccount2)\n\n        val result = accountsProcess.getGoogleAccounts(contextSupport).run\n        result shouldEqual Right(Seq(androidAccount1.name, androidAccount2.name))\n\n        there was one(mockAccountManager).getAccountsByType(accountType)\n      }\n\n    \"return an empty sequence and call with the right account type when account manager returns null\" in\n      new UserAccountsProcessImplScope {\n\n        mockAccountManager.getAccountsByType(any) returns javaNull\n\n        val result = accountsProcess.getGoogleAccounts(contextSupport).run\n        result shouldEqual Right(Seq.empty)\n\n        there was one(mockAccountManager).getAccountsByType(accountType)\n      }\n\n    \"return an UserAccountsProcessPermissionException when the getAccountsByType throw a SecurityException\" in\n      new UserAccountsProcessImplScope {\n\n        mockAccountManager.getAccountsByType(any) throws securityException\n\n        accountsProcess\n          .getGoogleAccounts(contextSupport)\n          .mustLeft[UserAccountsProcessPermissionException]\n\n        there was one(mockAccountManager).getAccountsByType(accountType)\n      }\n\n    \"return an UserAccountsProcessException when the getAccountsByType throw a RuntimeException\" in\n      new UserAccountsProcessImplScope {\n\n        mockAccountManager.getAccountsByType(any) throws runtimeException\n\n        accountsProcess.getGoogleAccounts(contextSupport).mustLeft[UserAccountsProcessException]\n\n        there was one(mockAccountManager).getAccountsByType(accountType)\n      }\n\n  }\n\n  \"getAuthToken\" should {\n\n    \"return the accounts sequence and call with the right account and scope\" in\n      new UserAccountsProcessImplScope {\n\n        contextSupport.getActivity returns Some(activity)\n        mockAccountManager.getAuthToken(any, any, any[Bundle], any[Activity], any, any).answers {\n          (params, mock) =>\n            params match {\n              case Array(_, _, _, _, callback: AccountManagerCallback[_], _) =>\n                callback.asInstanceOf[AccountManagerCallback[Bundle]].run(accountManagerFuture)\n                accountManagerFuture\n            }\n        }\n        accountManagerFuture.getResult returns bundle\n        bundle.getString(any) returns authToken\n\n        val result = accountsProcess.getAuthToken(accountName1, scope)(contextSupport).run\n        result shouldEqual Right(authToken)\n      }\n\n    \"return an UserAccountsProcessException when the activity is null\" in\n      new UserAccountsProcessImplScope {\n\n        contextSupport.getActivity returns None\n\n        accountsProcess\n          .getAuthToken(accountName1, scope)(contextSupport)\n          .mustLeft[UserAccountsProcessException]\n      }\n\n    \"return an UserAccountsProcessException when the token is null\" in\n      new UserAccountsProcessImplScope {\n\n        contextSupport.getActivity returns Some(activity)\n        mockAccountManager.getAuthToken(any, any, any[Bundle], any[Activity], any, any).answers {\n          (params, mock) =>\n            params match {\n              case Array(_, _, _, _, callback: AccountManagerCallback[_], _) =>\n                callback.asInstanceOf[AccountManagerCallback[Bundle]].run(accountManagerFuture)\n                accountManagerFuture\n            }\n        }\n        accountManagerFuture.getResult returns bundle\n        bundle.getString(any) returns javaNull\n\n        accountsProcess\n          .getAuthToken(accountName1, scope)(contextSupport)\n          .mustLeft[UserAccountsProcessException]\n      }\n\n    \"return an UserAccountsProcessException when the result is null\" in\n      new UserAccountsProcessImplScope {\n\n        contextSupport.getActivity returns Some(activity)\n        mockAccountManager.getAuthToken(any, any, any[Bundle], any[Activity], any, any).answers {\n          (params, mock) =>\n            params match {\n              case Array(_, _, _, _, callback: AccountManagerCallback[_], _) =>\n                callback.asInstanceOf[AccountManagerCallback[Bundle]].run(javaNull)\n                javaNull\n            }\n        }\n        accountsProcess\n          .getAuthToken(accountName1, scope)(contextSupport)\n          .mustLeft[UserAccountsProcessException]\n      }\n\n    \"return an UserAccountsProcessOperationCancelledException when the service throw an OperationCanceledException\" in\n      new UserAccountsProcessImplScope {\n\n        contextSupport.getActivity returns Some(activity)\n        mockAccountManager.getAuthToken(any, any, any[Bundle], any[Activity], any, any).answers {\n          (params, mock) =>\n            params match {\n              case Array(_, _, _, _, callback: AccountManagerCallback[_], _) =>\n                callback.asInstanceOf[AccountManagerCallback[Bundle]].run(accountManagerFuture)\n                accountManagerFuture\n            }\n        }\n        accountManagerFuture.getResult throws operationCancelledException\n\n        accountsProcess\n          .getAuthToken(accountName1, scope)(contextSupport)\n          .mustLeft[UserAccountsProcessOperationCancelledException]\n      }\n\n    \"return an UserAccountsProcessException when the service throw a RuntimeException\" in\n      new UserAccountsProcessImplScope {\n\n        contextSupport.getActivity returns Some(activity)\n        mockAccountManager.getAuthToken(any, any, any[Bundle], any[Activity], any, any).answers {\n          (params, mock) =>\n            params match {\n              case Array(_, _, _, _, callback: AccountManagerCallback[_], _) =>\n                callback.asInstanceOf[AccountManagerCallback[Bundle]].run(accountManagerFuture)\n                accountManagerFuture\n            }\n        }\n        accountManagerFuture.getResult throws runtimeException\n\n        accountsProcess\n          .getAuthToken(accountName1, scope)(contextSupport)\n          .mustLeft[UserAccountsProcessException]\n      }\n\n  }\n\n  \"invalidateToken\" should {\n\n    \"call invalidateToken in AccountManager with the right parameters\" in\n      new UserAccountsProcessImplScope {\n\n        val result = accountsProcess.invalidateToken(authToken)(contextSupport).run\n        result shouldEqual Right(())\n\n        there was one(mockAccountManager).invalidateAuthToken(androidAccount1.`type`, authToken)\n      }\n\n  }\n\n  \"havePermission\" should {\n\n    \"return true if the service return PermissionGranted for the specified permission\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.checkPermissions(any)(any) returns serviceRight(\n          Map(GetAccounts.value -> PermissionGranted))\n        val result = accountsProcess.havePermission(GetAccounts)(contextSupport).run\n\n        result shouldEqual Right(PermissionResult(GetAccounts, result = true))\n      }\n\n    \"return false if the service return PermissionDenied for the specified permission\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.checkPermissions(any)(any) returns serviceRight(\n          Map(GetAccounts.value -> PermissionDenied))\n        val result = accountsProcess.havePermission(GetAccounts)(contextSupport).run\n\n        result shouldEqual Right(PermissionResult(GetAccounts, result = false))\n      }\n\n    \"return false if the service return PermissionGranted for another permission\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.checkPermissions(any)(any) returns serviceRight(\n          Map(ReadContacts.value -> PermissionGranted))\n        val result = accountsProcess.havePermission(GetAccounts)(contextSupport).run\n\n        result shouldEqual Right(PermissionResult(GetAccounts, result = false))\n      }\n\n  }\n\n  \"havePermissions\" should {\n\n    \"return true for all permissions if the service return PermissionGranted for the permissions\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.checkPermissions(any)(any) returns\n          serviceRight(\n            Map(GetAccounts.value -> PermissionGranted, ReadContacts.value -> PermissionGranted))\n        val result =\n          accountsProcess.havePermissions(Seq(GetAccounts, ReadContacts))(contextSupport).run\n\n        result shouldEqual Right(\n          Seq(\n            PermissionResult(GetAccounts, result = true),\n            PermissionResult(ReadContacts, result = true)))\n      }\n\n    \"return true or false when the service returns PermissionGranted and PermissionDenied for the specified permissions\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.checkPermissions(any)(any) returns\n          serviceRight(\n            Map(GetAccounts.value -> PermissionGranted, ReadContacts.value -> PermissionDenied))\n        val result =\n          accountsProcess.havePermissions(Seq(GetAccounts, ReadContacts))(contextSupport).run\n\n        result shouldEqual Right(\n          Seq(\n            PermissionResult(GetAccounts, result = true),\n            PermissionResult(ReadContacts, result = false)))\n      }\n\n    \"return false for all permissions if the service return PermissionGranted for another permission\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.checkPermissions(any)(any) returns serviceRight(\n          Map(ReadCallLog.value -> PermissionGranted))\n        val result =\n          accountsProcess.havePermissions(Seq(GetAccounts, ReadContacts))(contextSupport).run\n\n        result shouldEqual Right(\n          Seq(\n            PermissionResult(GetAccounts, result = false),\n            PermissionResult(ReadContacts, result = false)))\n      }\n\n  }\n\n  \"shouldRequestPermission\" should {\n\n    \"return true if the service return true for the specified permission\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.shouldShowRequestPermissions(any)(any) returns\n          serviceRight(Map(GetAccounts.value -> true))\n        val result = accountsProcess.shouldRequestPermission(GetAccounts)(contextSupport).run\n\n        result shouldEqual Right(PermissionResult(GetAccounts, result = true))\n      }\n\n    \"return false if the service return false for the specified permission\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.shouldShowRequestPermissions(any)(any) returns\n          serviceRight(Map(GetAccounts.value -> false))\n        val result = accountsProcess.shouldRequestPermission(GetAccounts)(contextSupport).run\n\n        result shouldEqual Right(PermissionResult(GetAccounts, result = false))\n      }\n\n    \"return false if the service return true for another permission\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.shouldShowRequestPermissions(any)(any) returns\n          serviceRight(Map(ReadContacts.value -> true))\n        val result = accountsProcess.shouldRequestPermission(GetAccounts)(contextSupport).run\n\n        result shouldEqual Right(PermissionResult(GetAccounts, result = false))\n      }\n\n  }\n\n  \"shouldRequestPermissions\" should {\n\n    \"return true for all permissions if the service return true for the permissions\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.shouldShowRequestPermissions(any)(any) returns\n          serviceRight(Map(GetAccounts.value -> true, ReadContacts.value -> true))\n        val result = accountsProcess\n          .shouldRequestPermissions(Seq(GetAccounts, ReadContacts))(contextSupport)\n          .run\n\n        result shouldEqual Right(\n          Seq(\n            PermissionResult(GetAccounts, result = true),\n            PermissionResult(ReadContacts, result = true)))\n      }\n\n    \"return true or false when the service returns true and false for the specified permissions\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.shouldShowRequestPermissions(any)(any) returns\n          serviceRight(Map(GetAccounts.value -> true, ReadContacts.value -> false))\n        val result = accountsProcess\n          .shouldRequestPermissions(Seq(GetAccounts, ReadContacts))(contextSupport)\n          .run\n\n        result shouldEqual Right(\n          Seq(\n            PermissionResult(GetAccounts, result = true),\n            PermissionResult(ReadContacts, result = false)))\n      }\n\n    \"return false for all permissions if the service return true for another permission\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.shouldShowRequestPermissions(any)(any) returns serviceRight(\n          Map(ReadCallLog.value -> true))\n        val result = accountsProcess\n          .shouldRequestPermissions(Seq(GetAccounts, ReadContacts))(contextSupport)\n          .run\n\n        result shouldEqual Right(\n          Seq(\n            PermissionResult(GetAccounts, result = false),\n            PermissionResult(ReadContacts, result = false)))\n      }\n\n  }\n\n  \"requestPermission\" should {\n\n    \"call to the service with the right parameters\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.requestPermissions(any, any)(any) returns serviceRight((): Unit)\n        accountsProcess.requestPermission(permissionCode, GetAccounts)(contextSupport).run\n        there was one(permissionsServices)\n          .requestPermissions(permissionCode, Seq(GetAccounts.value))(contextSupport)\n      }\n\n  }\n\n  \"requestPermissions\" should {\n\n    \"call to the service with the right parameters\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.requestPermissions(any, any)(any) returns serviceRight((): Unit)\n        accountsProcess\n          .requestPermissions(permissionCode, Seq(GetAccounts, ReadContacts))(contextSupport)\n          .run\n        there was one(permissionsServices).requestPermissions(\n          permissionCode,\n          Seq(GetAccounts.value, ReadContacts.value))(contextSupport)\n      }\n\n  }\n\n  \"parsePermissionRequestResult\" should {\n\n    \"return true for all permissions if the service return PermissionGranted for the permissions\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.readPermissionsRequestResult(any, any) returns\n          serviceRight(\n            Map(GetAccounts.value -> PermissionGranted, ReadContacts.value -> PermissionGranted))\n        val result = accountsProcess\n          .parsePermissionsRequestResult(Array(GetAccounts.value, ReadContacts.value), Array.empty)\n          .run\n\n        result shouldEqual Right(\n          Seq(\n            PermissionResult(GetAccounts, result = true),\n            PermissionResult(ReadContacts, result = true)))\n      }\n\n    \"return true or false when the service returns PermissionGranted and PermissionDenied for the specified permissions\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.readPermissionsRequestResult(any, any) returns\n          serviceRight(\n            Map(GetAccounts.value -> PermissionGranted, ReadContacts.value -> PermissionDenied))\n        val result = accountsProcess\n          .parsePermissionsRequestResult(Array(GetAccounts.value, ReadContacts.value), Array.empty)\n          .run\n\n        result shouldEqual Right(\n          Seq(\n            PermissionResult(GetAccounts, result = true),\n            PermissionResult(ReadContacts, result = false)))\n      }\n\n    \"return false for all permissions if the service return PermissionGranted for another permission\" in\n      new UserAccountsProcessImplScope {\n\n        permissionsServices.readPermissionsRequestResult(any, any) returns serviceRight(\n          Map(ReadCallLog.value -> PermissionGranted))\n        val result = accountsProcess\n          .parsePermissionsRequestResult(Array(GetAccounts.value, ReadContacts.value), Array.empty)\n          .run\n\n        result shouldEqual Right(\n          Seq(\n            PermissionResult(GetAccounts, result = false),\n            PermissionResult(ReadContacts, result = false)))\n      }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/collection/impl/CollectionProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.collection.impl\n\nimport android.content.Intent\nimport android.content.pm.PackageManager\nimport android.content.res.Resources\nimport android.util.DisplayMetrics\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.ApplicationValues._\nimport cards.nine.commons.test.data.CardValues._\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.{ApiTestData, CollectionTestData}\nimport cards.nine.models.types.NineCardsMoment\nimport cards.nine.models.{CollectionProcessConfig, NineCardsIntent, RequestConfig}\nimport cards.nine.process.collection.{CardException, CollectionException}\nimport cards.nine.process.utils.ApiUtils\nimport cards.nine.services.api.{ApiServiceException, ApiServices}\nimport cards.nine.services.apps.{AppsInstalledException, AppsServices}\nimport cards.nine.services.awareness.AwarenessServices\nimport cards.nine.services.contacts.ContactsServices\nimport cards.nine.services.persistence._\nimport cards.nine.services.widgets.{WidgetServicesException, WidgetsServices}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait CollectionProcessImplSpecification\n    extends TaskServiceSpecification\n    with ApiTestData\n    with Mockito {\n\n  val persistenceServiceException = PersistenceServiceException(\"\")\n\n  val appsInstalledException = AppsInstalledException(\"\")\n\n  val apiServiceException = ApiServiceException(\"\")\n\n  val widgetServicesException = WidgetServicesException(\"\")\n\n  trait CollectionProcessScope extends Scope with CollectionTestData {\n\n    val resources = mock[Resources]\n    resources.getDisplayMetrics returns mock[DisplayMetrics]\n\n    val contextSupport = mock[ContextSupport]\n    contextSupport.getPackageManager returns mock[PackageManager]\n    contextSupport.getResources returns resources\n\n    val collectionProcessConfig = CollectionProcessConfig(Map.empty)\n\n    val mockPersistenceServices = mock[PersistenceServices]\n    val mockIntent              = mock[Intent]\n    val mockNineCardIntent      = mock[NineCardsIntent]\n\n    val mockAppsServices = mock[AppsServices]\n    mockAppsServices.getInstalledApplications(contextSupport) returns serviceRight(Seq.empty)\n\n    val mockContactsServices = mock[ContactsServices]\n    mockContactsServices.getFavoriteContacts returns serviceRight(Seq.empty)\n\n    val mockApiServices = mock[ApiServices]\n\n    val mockAwarenessServices = mock[AwarenessServices]\n\n    val mockWidgetServices = mock[WidgetsServices]\n\n    val mockApiUtils = mock[ApiUtils]\n\n    val mockRequestConfig = mock[RequestConfig]\n\n    mockApiUtils.getRequestConfig(any) returns serviceRight(mockRequestConfig)\n\n    val collectionProcess = new CollectionProcessImpl(\n      collectionProcessConfig = collectionProcessConfig,\n      persistenceServices = mockPersistenceServices,\n      contactsServices = mockContactsServices,\n      appsServices = mockAppsServices,\n      apiServices = mockApiServices,\n      awarenessServices = mockAwarenessServices,\n      widgetsServices = mockWidgetServices) {\n\n      override val apiUtils: ApiUtils = mockApiUtils\n\n    }\n  }\n\n}\n\nclass CollectionProcessImplSpec extends CollectionProcessImplSpecification {\n\n  \"getCollections\" should {\n\n    \"returns a sequence of collections for a valid request without cards\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceRight(\n          seqCollection.map(_.copy(cards = Seq.empty)))\n\n        collectionProcess.getCollections.mustRight { resultSeqCollection =>\n          resultSeqCollection.size shouldEqual seqCollection.size\n          resultSeqCollection map (_.name) shouldEqual seqCollection.map(_.name)\n          resultSeqCollection map (_.cards) shouldEqual Seq(Seq.empty, Seq.empty, Seq.empty)\n        }\n      }\n\n    \"returns a sequence of collections for a valid request \" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceRight(seqCollection)\n\n        collectionProcess.getCollections.mustRight { resultSeqCollection =>\n          resultSeqCollection.size shouldEqual seqCollection.size\n          resultSeqCollection map (_.name) shouldEqual seqCollection.map(_.name)\n          resultSeqCollection map (_.cards.size) shouldEqual seqCollection.map(_.cards.size)\n        }\n      }\n\n    \"returns a CollectionException if the service throws an exception\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceLeft(persistenceServiceException)\n        collectionProcess.getCollections.mustLeft[CollectionException]\n      }\n  }\n\n  \"getCollectionById\" should {\n\n    \"returns a collection for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(\n          Some(collection.copy(id = collectionId)))\n\n        collectionProcess.getCollectionById(collectionId).mustRight { resultCollection =>\n          resultCollection must beSome.which { collection =>\n            collection.name shouldEqual collection.name\n          }\n        }\n      }\n\n    \"returns None for a valid request if the collection id don't exists\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(None)\n        collectionProcess.getCollectionById(collectionId).mustRightNone\n      }\n\n    \"returns a CollectionException if the service throws an exception\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess.getCollectionById(collectionId).mustLeft[CollectionException]\n      }\n  }\n\n  \"getCollectionByCategory\" should {\n\n    \"returns a collection for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionByCategory(categoryStr) returns serviceRight(\n          Some(collection.copy(appsCategory = Option(category))))\n\n        collectionProcess.getCollectionByCategory(category).mustRight { resultCollection =>\n          resultCollection must beSome.which { collection =>\n            collection.name shouldEqual collection.name\n          }\n        }\n      }\n\n    \"returns None for a valid request if the collection id doesn't exists\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionByCategory(categoryStr) returns serviceRight(None)\n        collectionProcess.getCollectionByCategory(category).mustRightNone\n      }\n\n    \"returns a CollectionException if the service throws an exception\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionByCategory(categoryStr) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess.getCollectionByCategory(category).mustLeft[CollectionException]\n      }\n  }\n\n  \"getCollectionBySharedCollectionId\" should {\n\n    \"returns a collection for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(sharedCollectionId) returns serviceRight(\n          Option(collection.copy(sharedCollectionId = Option(sharedCollectionId))))\n\n        collectionProcess.getCollectionBySharedCollectionId(sharedCollectionId).mustRight {\n          resultCollection =>\n            resultCollection must beSome.which { collection =>\n              collection.name shouldEqual collection.name\n              collection.sharedCollectionId shouldEqual Option(sharedCollectionId)\n            }\n        }\n      }\n\n    \"returns None for a valid request if the collection id doesn't exists\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(sharedCollectionId) returns serviceRight(\n          None)\n        collectionProcess.getCollectionBySharedCollectionId(sharedCollectionId).mustRightNone\n      }\n\n    \"returns a CollectionException if the service throws an exception\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(sharedCollectionId) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess\n          .getCollectionBySharedCollectionId(sharedCollectionId)\n          .mustLeft[CollectionException]\n      }\n  }\n\n  \"createCollectionsFromFormedCollections\" should {\n\n    \"the size of collections should be equal to size of collections passed by parameter\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceRight(seqCollection)\n        mockPersistenceServices.addCollections(any) returns serviceRight(seqCollection)\n\n        collectionProcess\n          .createCollectionsFromCollectionData(seqCollectionData)(contextSupport)\n          .mustRight { resultSeqCollection =>\n            resultSeqCollection.size shouldEqual seqCollectionData.size\n            resultSeqCollection map (_.name) shouldEqual seqCollectionData.map(_.name)\n          }\n      }\n\n    \"returns CollectionExceptionImpl when persistence services fails\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceLeft(persistenceServiceException)\n        mockPersistenceServices.addCollections(any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess\n          .createCollectionsFromCollectionData(seqCollectionData)(contextSupport)\n          .mustLeft[CollectionException]\n      }\n\n  }\n\n  \"generatePrivateCollections\" should {\n\n    \"return a seq where the number of collections is equal to the non-empty categories\" in\n      new CollectionProcessScope {\n\n        val numCollections = seqApplicationData.groupBy(_.category).count(_._2.nonEmpty)\n        collectionProcess\n          .generatePrivateCollections(seqApplicationData)(contextSupport)\n          .mustRight(_.size shouldEqual numCollections)\n      }\n\n  }\n\n  \"addCollection\" should {\n\n    \"returns a the collection added for a valid request without cards\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceRight(\n          seqCollection map (_.copy(cards = Seq.empty)))\n        mockPersistenceServices.addCollection(any) returns serviceRight(\n          collection\n            .copy(id = seqCollection.size, position = seqCollection.size, cards = Seq.empty))\n\n        val result = collectionProcess.addCollection(collectionData.copy(cards = Seq.empty)).run\n        result shouldEqual Right(\n          collection\n            .copy(id = seqCollection.size, position = seqCollection.size, cards = Seq.empty))\n      }\n\n    \"returns a the collection added for a valid request with cards\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceRight(seqCollection)\n        mockPersistenceServices.addCollection(any) returns serviceRight(\n          collection.copy(id = seqCollection.size, position = seqCollection.size))\n        val result = collectionProcess.addCollection(collectionData).run\n        result shouldEqual Right(\n          collection.copy(id = seqCollection.size, position = seqCollection.size))\n      }\n\n    \"returns a CollectionException if service throws an exception fetching the collections\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceLeft(persistenceServiceException)\n        collectionProcess.addCollection(collectionData).mustLeft[CollectionException]\n      }\n\n    \"returns a CollectionException if the service throws an exception adding the new collection\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceRight(seqCollection)\n        mockPersistenceServices.addCollection(any) returns serviceLeft(persistenceServiceException)\n\n        collectionProcess.addCollection(collectionData).mustLeft[CollectionException]\n      }\n  }\n\n  \"deleteCollection\" should {\n\n    \"returns a successful answer for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(\n          Option(collection.copy()))\n        mockPersistenceServices.deleteCollection(any) returns serviceRight(deletedCollection)\n        mockPersistenceServices.deleteCardsByCollection(any) returns serviceRight(deletedCards)\n        mockPersistenceServices.fetchCollections returns serviceRight(seqCollection)\n        mockPersistenceServices.updateCollections(any) returns serviceRight(Seq(updatedCollection))\n\n        collectionProcess.deleteCollection(collectionId).mustRightUnit\n      }\n\n    \"returns a CollectionException if the service throws an exception finding the collection by Id\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess.deleteCollection(collectionId).mustLeft[CollectionException]\n      }\n\n    \"returns a CollectionException if the service throws an exception deleting the collection\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(\n          Option(collection.copy(id = collectionId)))\n        mockPersistenceServices.deleteCollection(any) returns serviceLeft(\n          persistenceServiceException)\n\n        collectionProcess.deleteCollection(collectionId).mustLeft[CollectionException]\n      }\n\n    \"returns a CollectionException if the service throws an exception deleting the cards by the collection\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(\n          Option(collection.copy(id = collectionId)))\n        mockPersistenceServices.deleteCollection(any) returns serviceRight(deletedCollection)\n        mockPersistenceServices.deleteCardsByCollection(any) returns serviceLeft(\n          persistenceServiceException)\n\n        collectionProcess.deleteCollection(collectionId).mustLeft[CollectionException]\n      }\n\n    \"returns a CollectionException if the service throws an exception fetching the collections\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(\n          Option(collection.copy(id = collectionId)))\n        mockPersistenceServices.deleteCollection(any) returns serviceRight(deletedCollection)\n        mockPersistenceServices.deleteCardsByCollection(any) returns serviceRight(deletedCards)\n        mockPersistenceServices.fetchCollections returns serviceLeft(persistenceServiceException)\n\n        collectionProcess.deleteCollection(collectionId).mustLeft[CollectionException]\n      }\n\n    \"returns a CollectionException if the service throws an exception updating the collections\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(\n          Option(collection.copy(id = collectionId)))\n        mockPersistenceServices.deleteCollection(any) returns serviceRight(deletedCollection)\n        mockPersistenceServices.deleteCardsByCollection(any) returns serviceRight(deletedCards)\n        mockPersistenceServices.fetchCollections returns serviceRight(seqCollection)\n        mockPersistenceServices.updateCollections(any) returns serviceLeft(\n          persistenceServiceException)\n\n        collectionProcess.deleteCollection(collectionId).mustLeft[CollectionException]\n      }\n  }\n\n  \"cleanCollections\" should {\n\n    \"returns a empty answer for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.deleteAllCollections() returns serviceRight(deletedCollection)\n        mockPersistenceServices.deleteAllCards() returns serviceRight(deletedCards)\n        collectionProcess.cleanCollections().mustRightUnit\n      }\n\n    \"returns a CollectionException if the service throws an exception removing collections\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.deleteAllCollections() returns serviceLeft(\n          persistenceServiceException)\n        mockPersistenceServices.deleteAllCards() returns serviceRight(deletedCards)\n        collectionProcess.cleanCollections().mustLeft[CollectionException]\n      }\n\n    \"returns a CollectionException if the service throws an exception removing cards\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.deleteAllCollections() returns serviceRight(deletedCollection)\n        mockPersistenceServices.deleteAllCards() returns serviceLeft(persistenceServiceException)\n        collectionProcess.cleanCollections().mustLeft[CollectionException]\n      }\n  }\n\n  \"reorderCollection\" should {\n\n    \"returns a empty answer for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceRight(seqCollection)\n        mockPersistenceServices.updateCollections(any) returns serviceRight(\n          Seq(updatedCollections))\n        collectionProcess.reorderCollection(0, collectionNewPosition).mustRightUnit\n      }\n\n    \"returns a CollectionException if the service throws an exception fetching the collection by position\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceLeft(persistenceServiceException)\n        collectionProcess.reorderCollection(0, collectionNewPosition).mustLeft[CollectionException]\n      }\n\n    \"returns a CollectionException if the service throws an exception fetching the collections\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceLeft(persistenceServiceException)\n        collectionProcess.reorderCollection(0, collectionNewPosition).mustLeft[CollectionException]\n      }\n\n    \"returns a CollectionException if the service throws an exception updating the collection\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceRight(seqCollection)\n        mockPersistenceServices.updateCollections(any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess.reorderCollection(0, collectionNewPosition).mustLeft[CollectionException]\n      }\n  }\n\n  \"editCollection\" should {\n\n    \"returns a the updated collection for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(Option(collection))\n        mockPersistenceServices.updateCollection(any) returns serviceRight(updatedCollection)\n        val editedCollectionData = collectionData.copy(\n          name = newCollectionName,\n          icon = newCollectionIcon,\n          themedColorIndex = newThemedColorIndex,\n          appsCategory = Option(category))\n        val result = collectionProcess.editCollection(collectionId, editedCollectionData).run\n        result shouldEqual Right(\n          collection.copy(\n            name = newCollectionName,\n            icon = newCollectionIcon,\n            themedColorIndex = newThemedColorIndex,\n            appsCategory = Option(category)))\n      }\n\n    \"returns a CollectionException if the service throws an exception finding the collection by Id\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess\n          .editCollection(collectionId, collectionData)\n          .mustLeft[CollectionException]\n      }\n\n    \"returns a CollectionException if the service throws an exception updating the collection\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(\n          Option(collection.copy(id = collectionId)))\n        mockPersistenceServices.updateCollection(any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess\n          .editCollection(collectionId, collectionData)\n          .mustLeft[CollectionException]\n      }\n  }\n\n  \"updateSharedCollection\" should {\n\n    \"returns a the updated collection for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(\n          Option(collection.copy(id = collectionId)))\n        mockPersistenceServices.updateCollection(any) returns serviceRight(updatedCollection)\n\n        val result =\n          collectionProcess.updateSharedCollection(collectionId, newSharedCollectionId).run\n        result shouldEqual Right(\n          collection.copy(sharedCollectionId = Option(newSharedCollectionId)))\n      }\n\n    \"returns a CollectionException if the service throws an exception finding the collection by Id\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess\n          .updateSharedCollection(collectionId, newSharedCollectionId)\n          .mustLeft[CollectionException]\n      }\n\n    \"returns a CollectionException if the service throws an exception updating the collection\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(\n          Option(collection.copy(id = collectionId)))\n        mockPersistenceServices.updateCollection(any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess\n          .updateSharedCollection(collectionId, newSharedCollectionId)\n          .mustLeft[CollectionException]\n      }\n  }\n\n  \"addPackages\" should {\n\n    \"returns a CollectionException when passing a collectionId that doesn't exists\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(None)\n        collectionProcess\n          .addPackages(collectionId, Seq.empty)(contextSupport)\n          .mustLeft[CollectionException]\n\n        there was one(mockPersistenceServices).findCollectionById(collectionId)\n      }\n\n    \"returns a Xor.Right[Unit] but doesn't call to persistence and api services when all applications are \" +\n      \"already included on the collection\" in new CollectionProcessScope {\n\n      mockPersistenceServices.findCollectionById(any) returns serviceRight(\n        Some(collection.copy(id = collectionId)))\n      mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(seqCard)\n\n      val result = collectionProcess\n        .addPackages(collectionId, seqCard.flatMap(_.packageName))(contextSupport)\n        .mustRightUnit\n\n      there was one(mockPersistenceServices).findCollectionById(collectionId)\n      there was one(mockPersistenceServices).fetchCardsByCollection(collectionId)\n      there was no(mockPersistenceServices).fetchAppByPackages(any)\n      there was no(mockApiServices).googlePlayPackagesDetail(any)(any)\n      there was no(mockPersistenceServices).addCards(any)\n    }\n\n    \"returns a Xor.Right[Unit] but doesn't call to api services when all applications are included on the collection \" +\n      \"or installed in the device\" in new CollectionProcessScope {\n\n      mockPersistenceServices.findCollectionById(any) returns serviceRight(\n        Some(collection.copy(id = collectionId)))\n      val (firstHalf, secondHalf) = seqCard.splitAt(seqCard.size / 2)\n      mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(firstHalf)\n      val secondHalfApps = seqApplication.filter(application =>\n        secondHalf.exists(_.packageName.contains(application.packageName)))\n      mockPersistenceServices.fetchAppByPackages(any) returns serviceRight(secondHalfApps)\n      mockPersistenceServices.addCards(any) returns serviceRight(secondHalf)\n\n      val result = collectionProcess\n        .addPackages(collectionId, seqCard.flatMap(_.packageName))(contextSupport)\n        .mustRightUnit\n\n      there was one(mockPersistenceServices).findCollectionById(collectionId)\n      there was one(mockPersistenceServices).fetchCardsByCollection(collectionId)\n      there was one(mockPersistenceServices).fetchAppByPackages(secondHalf.flatMap(_.packageName))\n      there was no(mockApiServices).googlePlayPackagesDetail(any)(any)\n      there was one(mockPersistenceServices).addCards(Seq((collectionId, seqCardData)))\n    }.pendingUntilFixed(\"Issue #943\")\n\n    \"returns a Xor.Right[Unit] and call to api services with the applications not installed on the device\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCollectionById(any) returns serviceRight(\n          Some(collection.copy(id = collectionId)))\n        val (firstHalf, secondHalf) = seqCard.splitAt(seqCard.size / 2)\n        mockPersistenceServices.fetchCardsByCollection(any) returns\n          serviceRight(firstHalf)\n        mockPersistenceServices.fetchAppByPackages(any) returns\n          serviceRight(Seq.empty)\n        val secondHalfPackages = categorizedDetailPackages.filter(p =>\n          secondHalf.exists(_.packageName.contains(p.packageName)))\n        mockApiServices.googlePlayPackagesDetail(any)(any) returns\n          serviceRight(secondHalfPackages)\n        mockPersistenceServices.addCards(any) returns\n          serviceRight(secondHalf)\n\n        collectionProcess\n          .addPackages(collectionId, seqCard.flatMap(_.packageName))(contextSupport)\n          .mustRightUnit\n\n        there was one(mockPersistenceServices).findCollectionById(collectionId)\n        there was one(mockPersistenceServices).fetchCardsByCollection(collectionId)\n        there was one(mockPersistenceServices).fetchAppByPackages(\n          secondHalf.flatMap(_.packageName))\n        there was one(mockApiServices).googlePlayPackagesDetail(secondHalf.flatMap(_.packageName))(\n          mockRequestConfig)\n        there was one(mockPersistenceServices).addCards(Seq((collectionId, seqCardData)))\n      }.pendingUntilFixed(\"Issue #943\")\n\n  }\n\n  \"rankApps\" should {\n\n    \"returns a the ordered packages for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceRight(seqApplication)\n        mockAwarenessServices.getLocation(any) returns serviceRight(awarenessLocation)\n        mockApiServices.rankApps(any, any)(any) returns serviceRight(seqRankApps)\n\n        collectionProcess.rankApps()(contextSupport).mustRight(_ shouldEqual seqPackagesByCategory)\n      }\n\n    \"returns a CollectionException if the service throws an exception getting the apps\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess.rankApps()(contextSupport).mustLeft[CollectionException]\n      }\n\n    \"returns the ordered packages even if the service throws an exception getting the country location\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceRight(seqApplication)\n        mockAwarenessServices.getLocation(any) returns serviceLeft(apiServiceException)\n        mockApiServices.rankApps(any, any)(any) returns serviceRight(seqRankApps)\n\n        collectionProcess.rankApps()(contextSupport).mustRight(_ shouldEqual seqPackagesByCategory)\n      }\n\n    \"returns a CollectionException if the service throws an exception updating the collection\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceRight(seqApplication)\n        mockAwarenessServices.getLocation(any) returns serviceRight(awarenessLocation)\n        mockApiServices.rankApps(any, any)(any) returns serviceLeft(apiServiceException)\n\n        collectionProcess.rankApps()(contextSupport).mustLeft[CollectionException]\n      }\n  }\n\n  \"rankAppsByMoment\" should {\n\n    \"returns a the ordered packages for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceRight(seqApplication)\n        mockPersistenceServices.fetchMoments returns serviceRight(seqMoment)\n        mockAwarenessServices.getLocation(any) returns serviceRight(awarenessLocation)\n        mockApiServices.rankAppsByMoment(any, any, any, any)(any) returns serviceRight(\n          seqRankAppsByMoment)\n\n        collectionProcess\n          .rankAppsByMoment(any)(contextSupport)\n          .mustRight(_ shouldEqual seqPackagesByMoment)\n      }\n\n    \"returns a CollectionException if the service throws an exception getting the apps\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess.rankAppsByMoment(any)(contextSupport).mustLeft[CollectionException]\n      }\n\n    \"returns a CollectionException if the service throws an exception getting the moments\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceRight(seqApplication)\n        mockPersistenceServices.fetchMoments returns serviceLeft(apiServiceException)\n        mockApiServices.rankAppsByMoment(any, any, any, any)(any) returns serviceRight(\n          seqRankAppsByMoment)\n\n        collectionProcess.rankAppsByMoment(any)(contextSupport).mustLeft[CollectionException]\n      }\n\n    \"returns the ordered packages even if the service throws an exception getting the country location\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceRight(seqApplication)\n        mockPersistenceServices.fetchMoments returns serviceRight(seqMoment)\n        mockAwarenessServices.getLocation(any) returns serviceLeft(apiServiceException)\n        mockApiServices.rankAppsByMoment(any, any, any, any)(any) returns serviceRight(\n          seqRankAppsByMoment)\n\n        collectionProcess\n          .rankAppsByMoment(any)(contextSupport)\n          .mustRight(_ shouldEqual seqPackagesByMoment)\n      }\n\n    \"returns a CollectionException if the service throws an exception updating the collection\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceRight(seqApplication)\n        mockPersistenceServices.fetchMoments returns serviceRight(seqMoment)\n        mockAwarenessServices.getLocation(any) returns serviceRight(awarenessLocation)\n        mockApiServices.rankAppsByMoment(any, any, any, any)(any) returns serviceLeft(\n          apiServiceException)\n\n        collectionProcess.rankAppsByMoment(any)(contextSupport).mustLeft[CollectionException]\n      }\n  }\n\n  \"rankWidgetsByMoment\" should {\n\n    \"returns a the ordered packages for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceRight(seqApplication)\n        mockAwarenessServices.getLocation(any) returns serviceRight(awarenessLocation)\n        mockWidgetServices.getWidgets(any) returns serviceRight(seqAppWidget)\n        mockApiServices.rankWidgetsByMoment(any, any, any, any)(any) returns serviceRight(\n          seqRankWidgetsByMoment)\n\n        collectionProcess\n          .rankWidgetsByMoment(any, NineCardsMoment.hourlyMoments)(contextSupport)\n          .mustRight(_ shouldEqual seqWidgetsByMoment)\n      }\n\n    \"returns a CollectionException if the service throws an exception getting the apps\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess\n          .rankWidgetsByMoment(any, NineCardsMoment.hourlyMoments)(contextSupport)\n          .mustLeft[CollectionException]\n      }\n\n    \"returns the ordered packages even if the service throws an exception getting the country location\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceRight(seqApplication)\n        mockAwarenessServices.getLocation(any) returns serviceLeft(apiServiceException)\n        mockWidgetServices.getWidgets(any) returns serviceRight(seqAppWidget)\n        mockApiServices.rankWidgetsByMoment(any, any, any, any)(any) returns serviceRight(\n          seqRankWidgetsByMoment)\n\n        collectionProcess\n          .rankWidgetsByMoment(any, NineCardsMoment.hourlyMoments)(contextSupport)\n          .mustRight(_ shouldEqual seqWidgetsByMoment)\n      }\n\n    \"returns a CollectionException if the service throws an exception getting the widgets\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceRight(seqApplication)\n        mockAwarenessServices.getLocation(any) returns serviceRight(awarenessLocation)\n        mockWidgetServices.getWidgets(any) returns serviceLeft(widgetServicesException)\n        mockApiServices.rankWidgetsByMoment(any, any, any, any)(any) returns serviceRight(\n          seqRankWidgetsByMoment)\n\n        collectionProcess\n          .rankWidgetsByMoment(any, NineCardsMoment.hourlyMoments)(contextSupport)\n          .mustLeft[CollectionException]\n      }\n\n    \"returns a CollectionException if the service throws an exception updating the collection\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns serviceRight(seqApplication)\n        mockAwarenessServices.getLocation(any) returns serviceRight(awarenessLocation)\n        mockWidgetServices.getWidgets(any) returns serviceRight(seqAppWidget)\n        mockApiServices.rankWidgetsByMoment(any, any, any, any)(any) returns serviceLeft(\n          apiServiceException)\n\n        collectionProcess\n          .rankWidgetsByMoment(any, NineCardsMoment.hourlyMoments)(contextSupport)\n          .mustLeft[CollectionException]\n      }\n  }\n\n  \"addCards\" should {\n\n    \"returns a sequence of cards for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(seqCard)\n        mockPersistenceServices.addCards(any) returns serviceRight(seqCard)\n\n        collectionProcess.addCards(collectionId, seqCardData).mustRight { resultCards =>\n          resultCards map (_.term) shouldEqual (seqCard map (_.term))\n        }\n      }\n\n    \"returns a CardException if service throws an exception fetching the cards\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess.addCards(collectionId, seqCardData).mustLeft[CardException]\n      }\n\n    \"returns an CardException if the service throws an exception adding the new cards\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(seqCard)\n        mockPersistenceServices.addCards(any) returns serviceLeft(persistenceServiceException)\n\n        collectionProcess.addCards(collectionId, seqCardData).mustLeft[CardException]\n      }\n  }\n\n  \"deleteCard\" should {\n\n    \"returns a successful answer for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.deleteCard(any, any) returns serviceRight(deletedCard)\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(seqCard)\n        mockPersistenceServices.updateCards(any) returns serviceRight(Seq(updatedCards))\n\n        collectionProcess.deleteCard(collectionId, cardId).mustRightUnit\n\n        there was one(mockPersistenceServices).updateCards(any)\n      }\n\n    \"returns a successful when return sequence empty\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.deleteCard(any, any) returns serviceRight(deletedCard)\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(Seq.empty)\n        mockPersistenceServices.updateCards(any) returns serviceRight(Seq(updatedCards))\n\n        collectionProcess.deleteCard(collectionId, cardId).mustRightUnit\n\n        there was one(mockPersistenceServices).updateCards(Seq.empty)\n      }\n\n    \"returns a CardException if the service throws an exception\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.deleteCard(any, any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess.deleteCard(collectionId, cardId).mustLeft[CardException]\n      }\n\n    \"returns a CardException if the service throws a exception\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.deleteCard(any, any) returns serviceRight(deletedCard)\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(seqCard)\n        mockPersistenceServices.updateCards(any) returns serviceLeft(persistenceServiceException)\n\n        collectionProcess.deleteCard(collectionId, cardId).mustLeft[CardException]\n      }\n  }\n\n  \"deleteAllCardsByPackageName\" should {\n\n    \"returns a successful when delete all Seq.empty of Cards in all collection by package name\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceRight(seqCollection)\n        collectionProcess.deleteAllCardsByPackageName(cardPackageName).mustRightUnit\n\n        there was no(mockPersistenceServices).deleteCard(any, any)\n        there was no(mockPersistenceServices).fetchCardsByCollection(any)\n      }\n\n    \"returns a CardException if the service throws a exception\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCollections returns serviceLeft(persistenceServiceException)\n        collectionProcess.deleteAllCardsByPackageName(cardPackageName).mustLeft[CardException]\n      }\n  }\n\n  \"deleteCards\" should {\n\n    \"returns a successful answer for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.deleteCards(any, any) returns serviceRight(deletedCards)\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(seqCard)\n        mockPersistenceServices.updateCards(any) returns serviceRight(Seq(updatedCards))\n\n        collectionProcess.deleteCards(collectionId, seqCard.map(_.id)).mustRightUnit\n      }\n\n    \"returns a successful when return sequence empty of cards\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.deleteCards(any, any) returns serviceRight(deletedCards)\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(Seq.empty)\n        mockPersistenceServices.updateCards(any) returns serviceRight(Seq(updatedCards))\n\n        collectionProcess.deleteCards(collectionId, seqCard.map(_.id)).mustRightUnit\n\n        there was one(mockPersistenceServices).updateCards(Seq.empty)\n      }\n    \"returns a CardException if the service throws a exception\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.deleteCards(any, any) returns serviceLeft(\n          persistenceServiceException)\n        collectionProcess.deleteCards(collectionId, seqCard.map(_.id)).mustLeft[CardException]\n      }\n\n    \"returns a CardException if the service throws a exception\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.deleteCards(any, any) returns serviceRight(cardId)\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(seqCard)\n        mockPersistenceServices.updateCards(any) returns serviceLeft(persistenceServiceException)\n\n        collectionProcess.deleteCards(collectionId, seqCard.map(_.id)).mustLeft[CardException]\n      }\n  }\n\n  \"reorderCard\" should {\n\n    \"returns a empty answer for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCardById(any) returns serviceRight(Option(card))\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(seqCard)\n        mockPersistenceServices.updateCards(any) returns serviceRight(Seq(updatedCards))\n\n        collectionProcess\n          .reorderCard(collectionId, cardIdReorder, newPositionReorder)\n          .mustRightUnit\n      }\n\n    \"returns an empty answer for a valid request, even if new position is the same\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCardById(any) returns serviceRight(Option(card))\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(seqCard)\n        mockPersistenceServices.updateCards(any) returns serviceRight(Seq(1))\n\n        collectionProcess\n          .reorderCard(collectionId, cardIdReorder, samePositionReorder)\n          .mustRightUnit\n      }\n\n    \"returns a CardException if the service throws an exception finding the card by Id\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCardById(any) returns serviceLeft(persistenceServiceException)\n        collectionProcess\n          .reorderCard(collectionId, cardIdReorder, newPositionReorder)\n          .mustLeft[CardException]\n      }\n\n    \"returns a CardException if the service throws an exception fetching the cards\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCardById(any) returns serviceRight(Option(card))\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceLeft(\n          persistenceServiceException)\n\n        collectionProcess\n          .reorderCard(collectionId, cardIdReorder, newPositionReorder)\n          .mustLeft[CardException]\n      }\n\n    \"returns a CardException if the service throws an exception updating the cards\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCardById(any) returns serviceRight(Option(card))\n        mockPersistenceServices.fetchCardsByCollection(any) returns serviceRight(seqCard)\n        mockPersistenceServices.updateCards(any) returns serviceLeft(persistenceServiceException)\n\n        collectionProcess\n          .reorderCard(collectionId, cardIdReorder, newPositionReorder)\n          .mustLeft[CardException]\n      }\n  }\n\n  \"editCard\" should {\n\n    \"returns a the updated card for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCardById(any) returns serviceRight(Option(card))\n        mockPersistenceServices.updateCard(any) returns serviceRight(updatedCard)\n\n        collectionProcess.editCard(collectionId, card.id, newCardName).mustRight { r =>\n          r shouldEqual card.copy(term = newCardName)\n        }\n      }\n\n    \"returns a CardException if the service throws an exception finding the collection by Id\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCardById(any) returns serviceLeft(persistenceServiceException)\n        collectionProcess.editCard(collectionId, card.id, newCardName).mustLeft[CardException]\n      }\n\n    \"returns a CardException if the service throws an exception updating the collection\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.findCardById(any) returns serviceRight(Option(card))\n        mockPersistenceServices.updateCard(any) returns serviceLeft(persistenceServiceException)\n\n        collectionProcess.editCard(collectionId, card.id, newCardName).mustLeft[CardException]\n      }\n  }\n\n  \"updateNoInstalledCardsInCollections\" should {\n\n    \"returns Unit if the updated card for a valid request\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCards returns serviceRight(seqCard)\n        mockPersistenceServices.updateCards(any) returns serviceRight(Seq(updatedCards))\n        mockAppsServices.getApplication(applicationPackageName)(contextSupport) returns serviceRight(\n          applicationData)\n\n        collectionProcess\n          .updateNoInstalledCardsInCollections(applicationPackageName)(contextSupport)\n          .mustRightUnit\n      }\n\n    \"returns a CardException if the service throws an exception updating the collection\" in\n      new CollectionProcessScope {\n\n        mockPersistenceServices.fetchCards returns serviceRight(seqCard)\n        mockPersistenceServices.updateCard(any) returns serviceRight(updatedCard)\n        mockAppsServices.getApplication(applicationPackageName)(contextSupport) returns serviceLeft(\n          appsInstalledException)\n\n        collectionProcess\n          .updateNoInstalledCardsInCollections(applicationPackageName)(contextSupport)\n          .mustLeft[CardException]\n      }\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/device/impl/DeviceProcessData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device.impl\n\nimport cards.nine.commons._\nimport cards.nine.commons.test.data.{ApplicationTestData, DeviceTestData}\nimport cards.nine.models._\nimport cards.nine.repository.model.{App => RepositoryApp}\nimport cards.nine.services.persistence.conversions.AppConversions\n\ntrait DeviceProcessData\n    extends ApplicationTestData\n    with DeviceTestData\n    with AppConversions\n    with NineCardsIntentConversions {\n\n  val iterableCursorContact = new IterableCursor[Contact] {\n    override def count(): Int = seqContact.length\n\n    override def moveToPosition(pos: Int): Contact = seqContact(pos)\n\n    override def close(): Unit = ()\n  }\n\n  val iterableContact = new IterableContacts(iterableCursorContact)\n\n  val mockIterableCursor = new IterableCursor[RepositoryApp] {\n    override def count(): Int = 0\n\n    override def moveToPosition(pos: Int): RepositoryApp = javaNull\n\n    override def close(): Unit = ()\n  }\n\n  val iterableCursorApps = new IterableAppCursor(mockIterableCursor, toApp) {\n    override def count(): Int = seqApplication.length\n\n    override def moveToPosition(pos: Int): ApplicationData = seqApplicationData(pos)\n\n    override def close(): Unit = ()\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/device/impl/DeviceProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.device.impl\n\nimport android.content.ComponentName\nimport android.content.Intent.ShortcutIconResource\nimport android.content.pm.PackageManager\nimport android.content.res.Resources\nimport android.graphics.Bitmap\nimport android.graphics.drawable.Drawable\nimport android.util.DisplayMetrics\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.javaNull\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.ApplicationValues._\nimport cards.nine.commons.test.data.CardValues._\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.DeviceValues._\nimport cards.nine.commons.test.data.DockAppValues._\nimport cards.nine.commons.test.data.WidgetValues._\nimport cards.nine.commons.test.data._\nimport cards.nine.models.BitmapPath\nimport cards.nine.models.types._\nimport cards.nine.process.device._\nimport cards.nine.process.utils.ApiUtils\nimport cards.nine.services.api._\nimport cards.nine.services.apps.{AppsInstalledException, AppsServices}\nimport cards.nine.services.calls.{CallsServices, CallsServicesException}\nimport cards.nine.services.contacts.{ContactsServiceException, ContactsServices}\nimport cards.nine.services.image._\nimport cards.nine.services.persistence._\nimport cards.nine.services.shortcuts.{ShortcutServicesException, ShortcutsServices}\nimport cards.nine.services.widgets.{WidgetServicesException, WidgetsServices}\nimport cards.nine.services.connectivity.ConnectivityServices\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\nimport scala.util.Right\n\ntrait DeviceProcessSpecification\n    extends Specification\n    with ApiTestData\n    with ApplicationTestData\n    with AppWidgetTestData\n    with DeviceTestData\n    with DockAppTestData\n    with Mockito {\n\n  val appInstalledException = AppsInstalledException(\"\")\n\n  val apiServiceException = ApiServiceException(\"\")\n\n  val persistenceServiceException = PersistenceServiceException(\"\")\n\n  val bitmapTransformationException = BitmapTransformationException(\"\")\n\n  val shortcutServicesException = ShortcutServicesException(\"\")\n\n  val contactsServicesException = ContactsServiceException(\"\")\n\n  val fileServicesException = FileException(\"\")\n\n  val widgetsServicesException = WidgetServicesException(\"\")\n\n  val callsServicesException = CallsServicesException(\"\")\n\n  trait DeviceProcessScope extends Scope with DeviceProcessData {\n\n    val resources = mock[Resources]\n    resources.getDisplayMetrics returns mock[DisplayMetrics]\n\n    val mockPackageManager = mock[PackageManager]\n    mockPackageManager.getActivityIcon(any[ComponentName]) returns javaNull\n\n    val contextSupport = mock[ContextSupport]\n    contextSupport.getPackageManager returns mockPackageManager\n    contextSupport.getResources returns resources\n\n    val mockBitmap = mock[Bitmap]\n\n    val mockIcon = mock[Drawable]\n\n    val mockAppsServices = mock[AppsServices]\n\n    val mockApiServices = mock[ApiServices]\n\n    val mockShortcutsServices = mock[ShortcutsServices]\n\n    val mockPersistenceServices = mock[PersistenceServices]\n\n    val mockContactsServices = mock[ContactsServices]\n\n    val mockImageServices = mock[ImageServices]\n\n    val mockWidgetsServices = mock[WidgetsServices]\n\n    val mockCallsServices = mock[CallsServices]\n\n    val mockConnectivityServices = mock[ConnectivityServices]\n\n    val deviceProcess = new DeviceProcessImpl(\n      mockAppsServices,\n      mockApiServices,\n      mockPersistenceServices,\n      mockShortcutsServices,\n      mockContactsServices,\n      mockImageServices,\n      mockWidgetsServices,\n      mockCallsServices,\n      mockConnectivityServices) {\n\n      override val apiUtils: ApiUtils = mock[ApiUtils]\n\n      apiUtils.getRequestConfig(contextSupport) returns TaskService(\n        Task(Either.right(requestConfig)))\n\n    }\n\n    val mockShortcutIconResource = mock[ShortcutIconResource]\n\n  }\n\n}\n\nclass DeviceProcessImplSpec extends DeviceProcessSpecification {\n\n  \"Delete saved items\" should {\n\n    \"deletes all apps, cards, collections and dockApps\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.deleteAllWidgets() returns TaskService(\n          Task(Either.right(deletedWidgets)))\n        mockPersistenceServices.deleteAllCollections() returns TaskService(\n          Task(Either.right(deletedCollections)))\n        mockPersistenceServices.deleteAllCards() returns TaskService(\n          Task(Either.right(deletedCards)))\n        mockPersistenceServices.deleteAllDockApps() returns TaskService(\n          Task(Either.right(deletedDockApps)))\n\n        val result = deviceProcess.resetSavedItems().value.run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"returns ResetException when persistence service fails deleting widgets\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.deleteAllWidgets() returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n\n        val result = deviceProcess.resetSavedItems().value.run\n        result must beAnInstanceOf[Left[ResetException, _]]\n      }\n\n    \"returns ResetException when persistence service fails deleting the collections\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.deleteAllWidgets() returns TaskService(\n          Task(Either.right(deletedWidgets)))\n        mockPersistenceServices.deleteAllCollections returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n\n        val result = deviceProcess.resetSavedItems().value.run\n        result must beAnInstanceOf[Left[ResetException, _]]\n      }\n\n    \"returns ResetException when persistence service fails deleting the cards\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.deleteAllWidgets() returns TaskService(\n          Task(Either.right(deletedWidgets)))\n        mockPersistenceServices.deleteAllCollections returns TaskService(\n          Task(Either.right(deletedCollections)))\n        mockPersistenceServices.deleteAllCards returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n\n        val result = deviceProcess.resetSavedItems().value.run\n        result must beAnInstanceOf[Left[ResetException, _]]\n      }\n\n    \"returns ResetException when persistence service fails deleting the dock apps\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.deleteAllWidgets() returns TaskService(\n          Task(Either.right(deletedWidgets)))\n        mockPersistenceServices.deleteAllCollections() returns TaskService(\n          Task(Either.right(deletedCollections)))\n        mockPersistenceServices.deleteAllCards() returns TaskService(\n          Task(Either.right(deletedCards)))\n        mockPersistenceServices.deleteAllDockApps() returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n\n        val result = deviceProcess.resetSavedItems().value.run\n        result must beAnInstanceOf[Left[ResetException, _]]\n      }\n  }\n\n  \"Get Shortcuts\" should {\n\n    \"get available Shortcuts\" in\n      new DeviceProcessScope {\n\n        val shortcutsWithIcon = seqShortcut.map(_.copy(icon = Option(mockIcon)))\n\n        mockShortcutsServices.getShortcuts(contextSupport) returns TaskService(\n          Task(Either.right(shortcutsWithIcon)))\n        val result = deviceProcess.getAvailableShortcuts(contextSupport).value.run\n        result must beLike {\n          case Right(r) => r shouldEqual shortcutsWithIcon\n        }\n      }\n\n    \"returns ShortcutException when ShortcutsServices fails\" in\n      new DeviceProcessScope {\n\n        mockShortcutsServices.getShortcuts(contextSupport) returns TaskService(\n          Task(Either.left(shortcutServicesException)))\n        val result = deviceProcess.getAvailableShortcuts(contextSupport).value.run\n        result must beAnInstanceOf[Left[ShortcutException, _]]\n      }\n\n  }\n\n  \"Get Favorite Contacts\" should {\n\n    \"get favorite contacts\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getFavoriteContacts returns TaskService(\n          Task(Either.right(seqContact)))\n        mockContactsServices.populateContactInfo(any) returns TaskService(\n          Task(Either.right(seqContact)))\n\n        val result = deviceProcess.getFavoriteContacts(contextSupport).value.run\n        result shouldEqual Right(seqContact)\n      }\n\n    \"returns ContactException when ContactsServices fails getting the favorite contacts\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getFavoriteContacts returns TaskService(\n          Task(Either.left(contactsServicesException)))\n        val result = deviceProcess.getFavoriteContacts(contextSupport).value.run\n        result must beAnInstanceOf[Left[ContactException, _]]\n      }\n\n    \"returns ContactException when ContactsServices fails filling the contacts\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getFavoriteContacts returns TaskService(\n          Task(Either.right(seqContact)))\n        mockContactsServices.populateContactInfo(any) returns TaskService(\n          Task(Either.left(contactsServicesException)))\n        val result = deviceProcess.getFavoriteContacts(contextSupport).value.run\n        result must beAnInstanceOf[Left[ContactException, _]]\n      }\n\n  }\n\n  \"getCounterForIterableContacts\" should {\n\n    \"get term counters for contacts by name\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getAlphabeticalCounterContacts returns TaskService(\n          Task(Either.right(contactsCounters)))\n        val result = deviceProcess.getTermCountersForContacts()(contextSupport).value.run\n        result must beLike {\n          case Right(counters) =>\n            counters map (_.term) shouldEqual (contactsCounters map (_.term))\n        }\n        there was one(mockContactsServices).getAlphabeticalCounterContacts\n      }\n\n    \"get term counters for contacts by favorite\" in\n      new DeviceProcessScope {\n\n        val result =\n          deviceProcess.getTermCountersForContacts(FavoriteContacts)(contextSupport).value.run\n        result must beLike {\n          case Right(counters) => counters shouldEqual Seq.empty\n        }\n      }\n\n    \"get term counters for apps by contacts with phone number\" in\n      new DeviceProcessScope {\n\n        val result = deviceProcess\n          .getTermCountersForContacts(ContactsWithPhoneNumber)(contextSupport)\n          .value\n          .run\n        result must beLike {\n          case Right(counters) => counters shouldEqual Seq.empty\n        }\n      }\n\n    \"returns AppException if persistence service fails \" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchAlphabeticalAppsCounter returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        mockPersistenceServices.fetchCategorizedAppsCounter returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        //        mockPersistenceServices.fetchIterableAppsByKeyword(any, any, any) returns TaskService(Task(Either.left(persistenceServiceException)))\n\n        val result = deviceProcess.getTermCountersForApps(GetByName)(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n  }\n\n  \"Save shortcut icon\" should {\n\n    \"get path of icon stored\" in\n      new DeviceProcessScope {\n\n        val bitmapPath = BitmapPath(shortcutName, fileNameShortcut)\n        mockImageServices.saveBitmap(mockBitmap, None, None)(contextSupport) returns TaskService(\n          Task(Either.right(bitmapPath)))\n\n        val result = deviceProcess.saveShortcutIcon(mockBitmap)(contextSupport).value.run\n        result must beLike {\n          case Right(path) => path shouldEqual fileNameShortcut\n        }\n      }\n\n    \"returns ShortcutException when ImageServices fails storing the icon\" in\n      new DeviceProcessScope {\n\n        mockImageServices.saveBitmap(any, any, any)(any) returns TaskService(\n          Task(Either.left(fileServicesException)))\n        val result = deviceProcess.saveShortcutIcon(mockBitmap)(contextSupport).value.run\n        result must beAnInstanceOf[Left[ShortcutException, _]]\n      }\n  }\n\n  \"Decode shortcut icon\" should {\n\n    \"get the Bitmap of the icon resource\" in\n      new DeviceProcessScope {\n\n        mockImageServices.decodeShortcutIconResource(any)(any) returns\n          TaskService(Task(Either.right(mockBitmap)))\n\n        val result =\n          deviceProcess.decodeShortcutIcon(mockShortcutIconResource)(contextSupport).value.run\n        result shouldEqual Right(mockBitmap)\n      }\n\n    \"returns ShortcutException when ImageServices fails decoding the icon\" in\n      new DeviceProcessScope {\n\n        mockImageServices.decodeShortcutIconResource(any)(any) returns TaskService(\n          Task(Either.left(bitmapTransformationException)))\n        val result =\n          deviceProcess.decodeShortcutIcon(mockShortcutIconResource)(contextSupport).value.run\n        result must beAnInstanceOf[Left[ShortcutException, _]]\n      }\n  }\n\n  \"Get Contacts Sorted By Name\" should {\n\n    \"get all contacts sorted\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getContacts returns TaskService(Task(Either.right(seqContact)))\n        mockContactsServices.getFavoriteContacts returns TaskService(\n          Task(Either.right(seqContact)))\n\n        val result = deviceProcess.getContacts()(contextSupport).value.run\n        result must beLike {\n          case Right(response) => response.map(_.name) shouldEqual seqContact.map(_.name)\n        }\n      }\n\n    \"get favorite contacts sorted\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getContacts returns TaskService(Task(Either.right(seqContact)))\n        mockContactsServices.getFavoriteContacts returns TaskService(\n          Task(Either.right(seqContact)))\n\n        val result = deviceProcess.getContacts(FavoriteContacts)(contextSupport).value.run\n        result must beLike {\n          case Right(response) => response.map(_.name) shouldEqual seqContact.map(_.name)\n        }\n      }\n\n    \"get contacts with phone number sorted\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getContacts returns TaskService(Task(Either.right(seqContact)))\n        mockContactsServices.getFavoriteContacts returns TaskService(\n          Task(Either.right(seqContact)))\n        mockContactsServices.getContactsWithPhone returns TaskService(\n          Task(Either.right(seqContact)))\n\n        val result = deviceProcess.getContacts(ContactsWithPhoneNumber)(contextSupport).value.run\n        result must beLike {\n          case Right(response) => response.map(_.name) shouldEqual seqContact.map(_.name)\n        }\n      }\n\n    \"returns ContactException when ContactsService fails getting contacts\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getContacts returns TaskService(\n          Task(Either.left(contactsServicesException)))\n        mockContactsServices.getFavoriteContacts returns TaskService(\n          Task(Either.right(seqContact)))\n        mockContactsServices.getContactsWithPhone returns TaskService(\n          Task(Either.right(seqContact)))\n\n        val result = deviceProcess.getContacts()(contextSupport).value.run\n        result must beAnInstanceOf[Left[ContactException, _]]\n      }\n\n  }\n\n  \"Get Iterable Contacts Sorted By Name\" should {\n\n    \"get all contacts sorted\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getIterableContacts returns TaskService(\n          Task(Either.right(iterableCursorContact)))\n        mockContactsServices.getIterableFavoriteContacts returns TaskService(\n          Task(Either.right(iterableCursorContact)))\n        val result = deviceProcess.getIterableContacts()(contextSupport).value.run\n        result must beLike {\n          case Right(iter) => iter.moveToPosition(0) shouldEqual iterableContact.moveToPosition(0)\n        }\n      }\n\n    \"get favorite contacts sorted\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getIterableFavoriteContacts returns TaskService(\n          Task(Either.right(iterableCursorContact)))\n        val result = deviceProcess.getIterableContacts(FavoriteContacts)(contextSupport).value.run\n        result must beLike {\n          case Right(iter) => iter.moveToPosition(0) shouldEqual iterableContact.moveToPosition(0)\n        }\n      }\n\n    \"get contacts with phone number sorted\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getIterableContactsWithPhone returns TaskService(\n          Task(Either.right(iterableCursorContact)))\n        val result =\n          deviceProcess.getIterableContacts(ContactsWithPhoneNumber)(contextSupport).value.run\n        result must beLike {\n          case Right(iter) => iter.moveToPosition(0) shouldEqual iterableContact.moveToPosition(0)\n        }\n      }\n\n    \"returns ContactException when ContactsService fails getting contacts\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getIterableContacts returns TaskService(\n          Task(Either.left(contactsServicesException)))\n        val result = deviceProcess.getIterableContacts()(contextSupport).value.run\n        result must beAnInstanceOf[Left[ContactException, _]]\n      }\n\n  }\n\n  \"Get Contact\" should {\n\n    \"get contact find a contact with data info filled\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.findContactByLookupKey(anyString) returns TaskService(\n          Task(Either.right(contact)))\n        val result = deviceProcess.getContact(contact.lookupKey)(contextSupport).value.run\n        result must beLike {\n          case Right(response) =>\n            response.lookupKey shouldEqual contact.lookupKey\n            response.info must beSome\n        }\n      }\n\n    \"returns ContactException when ContactsService fails getting contact\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.findContactByLookupKey(anyString) returns TaskService(\n          Task(Either.left(contactsServicesException)))\n        val result = deviceProcess.getContact(contact.lookupKey)(contextSupport).value.run\n        result must beAnInstanceOf[Left[ContactException, _]]\n      }\n\n  }\n\n  \"Get Iterable Contacts by keyword\" should {\n\n    \"get contacts by keyword\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getIterableContactsByKeyword(contactKeyword) returns TaskService(\n          Task(Either.right(iterableCursorContact)))\n        val result =\n          deviceProcess.getIterableContactsByKeyWord(contactKeyword)(contextSupport).value.run\n        result must beLike {\n          case Right(iter) => iter.moveToPosition(0) shouldEqual iterableContact.moveToPosition(0)\n        }\n      }\n\n    \"returns ContactException when ContactsService fails getting contacts\" in\n      new DeviceProcessScope {\n\n        mockContactsServices.getIterableContactsByKeyword(contactKeyword) returns TaskService(\n          Task(Either.left(contactsServicesException)))\n        val result =\n          deviceProcess.getIterableContactsByKeyWord(contactKeyword)(contextSupport).value.run\n        result must beAnInstanceOf[Left[ContactException, _]]\n      }\n\n  }\n\n  \"Get Saved Apps\" should {\n\n    \"get saved apps by name\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns TaskService(\n          Task(Either.right(seqApplication)))\n        val result = deviceProcess.getSavedApps(GetByName)(contextSupport).value.run\n        result shouldEqual Right(seqApplicationData)\n        there was one(mockPersistenceServices).fetchApps(OrderByName, ascending = true)\n      }\n\n    \"get saved apps by update date\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns TaskService(\n          Task(Either.right(seqApplication)))\n        val result = deviceProcess.getSavedApps(GetByInstallDate)(contextSupport).value.run\n        result shouldEqual Right(seqApplicationData)\n        there was one(mockPersistenceServices).fetchApps(OrderByInstallDate, ascending = false)\n      }\n\n    \"get saved apps by category\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns TaskService(\n          Task(Either.right(seqApplication)))\n        val result = deviceProcess.getSavedApps(GetByCategory)(contextSupport).value.run\n        result shouldEqual Right(seqApplicationData)\n        there was one(mockPersistenceServices).fetchApps(OrderByCategory, ascending = true)\n      }\n\n    \"returns AppException if persistence service fails \" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = deviceProcess.getSavedApps(GetByName)(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n  }\n\n  \"Get Iterable Saved Apps\" should {\n\n    \"get iterable saved apps by name\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchIterableApps(any, any) returns TaskService(\n          Task(Either.right(iterableCursorApps)))\n        mockPersistenceServices.fetchIterableAppsByCategory(any, any, any) returns TaskService(\n          Task(Either.right(iterableCursorApps)))\n\n        val result = deviceProcess.getIterableApps(GetByName)(contextSupport).value.run\n        result must beLike {\n          case Right(iter) =>\n            iter.moveToPosition(0) shouldEqual iterableCursorApps.moveToPosition(0)\n        }\n        there was one(mockPersistenceServices).fetchIterableApps(OrderByName, ascending = true)\n      }\n\n    \"get iterable saved apps by update date\" in\n      new DeviceProcessScope {\n        mockPersistenceServices.fetchIterableApps(any, any) returns TaskService(\n          Task(Either.right(iterableCursorApps)))\n\n        val result = deviceProcess.getIterableApps(GetByInstallDate)(contextSupport).value.run\n        result must beLike {\n          case Right(iter) =>\n            iter.moveToPosition(0) shouldEqual iterableCursorApps.moveToPosition(0)\n        }\n        there was one(mockPersistenceServices)\n          .fetchIterableApps(OrderByInstallDate, ascending = false)\n      }\n\n    \"get iterable saved apps by category\" in\n      new DeviceProcessScope {\n        mockPersistenceServices.fetchIterableApps(any, any) returns TaskService(\n          Task(Either.right(iterableCursorApps)))\n        mockPersistenceServices.fetchAlphabeticalAppsCounter returns TaskService(\n          Task(Either.right(appsCounters)))\n        mockPersistenceServices.fetchCategorizedAppsCounter returns TaskService(\n          Task(Either.right(categoryCounters)))\n        mockPersistenceServices.fetchInstallationDateAppsCounter returns TaskService(\n          Task(Either.right(installationAppsCounters)))\n\n        val result = deviceProcess.getIterableApps(GetByCategory)(contextSupport).value.run\n        result must beLike {\n          case Right(iter) =>\n            iter.moveToPosition(0) shouldEqual iterableCursorApps.moveToPosition(0)\n        }\n        there was one(mockPersistenceServices).fetchIterableApps(OrderByCategory, ascending = true)\n      }\n\n    \"returns AppException if persistence service fails \" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchIterableApps(any, any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = deviceProcess.getIterableApps(GetByName)(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n  }\n\n  \"Get Iterable Saved Apps By Category\" should {\n\n    \"get iterable saved apps by category\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchIterableAppsByCategory(any, any, any) returns TaskService(\n          Task(Either.right(iterableCursorApps)))\n        val result = deviceProcess.getIterableAppsByCategory(categoryStr)(contextSupport).value.run\n        result must beLike {\n          case Right(iter) =>\n            iter.moveToPosition(0) shouldEqual iterableCursorApps.moveToPosition(0)\n        }\n        there was one(mockPersistenceServices)\n          .fetchIterableAppsByCategory(categoryStr, OrderByName, ascending = true)\n      }\n\n    \"returns AppException if persistence service fails \" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchIterableAppsByCategory(any, any, any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = deviceProcess.getIterableAppsByCategory(categoryStr)(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n  }\n\n  \"getTermCountersForApps\" should {\n\n    \"get term counters for apps by name\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchAlphabeticalAppsCounter returns TaskService(\n          Task(Either.right(appsCounters)))\n        mockPersistenceServices.fetchCategorizedAppsCounter returns TaskService(\n          Task(Either.right(categoryCounters)))\n        mockPersistenceServices.fetchInstallationDateAppsCounter returns TaskService(\n          Task(Either.right(installationAppsCounters)))\n\n        val result = deviceProcess.getTermCountersForApps(GetByName)(contextSupport).value.run\n        result must beLike {\n          case Right(counters) =>\n            counters map (_.term) shouldEqual (appsCounters map (_.term))\n        }\n        there was one(mockPersistenceServices).fetchAlphabeticalAppsCounter\n      }\n\n    \"get term counters for apps by installation date\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchAlphabeticalAppsCounter returns TaskService(\n          Task(Either.right(appsCounters)))\n        mockPersistenceServices.fetchCategorizedAppsCounter returns TaskService(\n          Task(Either.right(categoryCounters)))\n        mockPersistenceServices.fetchInstallationDateAppsCounter returns TaskService(\n          Task(Either.right(installationAppsCounters)))\n\n        val result =\n          deviceProcess.getTermCountersForApps(GetByInstallDate)(contextSupport).value.run\n        result must beLike {\n          case Right(counters) =>\n            counters map (_.term) shouldEqual (installationAppsCounters map (_.term))\n        }\n      }\n\n    \"get term counters for apps by category\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchAlphabeticalAppsCounter returns TaskService(\n          Task(Either.right(appsCounters)))\n        mockPersistenceServices.fetchCategorizedAppsCounter returns TaskService(\n          Task(Either.right(categoryCounters)))\n        mockPersistenceServices.fetchInstallationDateAppsCounter returns TaskService(\n          Task(Either.right(installationAppsCounters)))\n\n        val result = deviceProcess.getTermCountersForApps(GetByCategory)(contextSupport).value.run\n        result must beLike {\n          case Right(counters) =>\n            counters map (_.term) shouldEqual (categoryCounters map (_.term))\n        }\n        there was one(mockPersistenceServices).fetchCategorizedAppsCounter\n      }\n\n    \"returns AppException if persistence service fails in GetByName\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchAlphabeticalAppsCounter returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        mockPersistenceServices.fetchCategorizedAppsCounter returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        mockPersistenceServices.fetchInstallationDateAppsCounter returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n\n        val result = deviceProcess.getTermCountersForApps(GetByName)(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n    \"returns AppException if persistence service fails in GetByCategory\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchAlphabeticalAppsCounter returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        mockPersistenceServices.fetchCategorizedAppsCounter returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        mockPersistenceServices.fetchInstallationDateAppsCounter returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n\n        val result = deviceProcess.getTermCountersForApps(GetByCategory)(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n    \"returns AppException if persistence service fails in GetByInstallDate\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchAlphabeticalAppsCounter returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        mockPersistenceServices.fetchCategorizedAppsCounter returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        mockPersistenceServices.fetchInstallationDateAppsCounter returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n\n        val result =\n          deviceProcess.getTermCountersForApps(GetByInstallDate)(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n  }\n\n  \"Get Iterable Apps by keyword\" should {\n\n    \"get iterable apps ordered by name\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchIterableAppsByKeyword(any, any, any) returns TaskService(\n          Task(Either.right(iterableCursorApps)))\n        val result =\n          deviceProcess.getIterableAppsByKeyWord(appKeyword, GetByName)(contextSupport).value.run\n        result must beLike {\n          case Right(iter) =>\n            iter.moveToPosition(0) shouldEqual iterableCursorApps.moveToPosition(0)\n        }\n        there was one(mockPersistenceServices)\n          .fetchIterableAppsByKeyword(appKeyword, OrderByName, ascending = true)\n      }\n\n    \"get iterable apps ordered by update date\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchIterableAppsByKeyword(any, any, any) returns TaskService(\n          Task(Either.right(iterableCursorApps)))\n        val result = deviceProcess\n          .getIterableAppsByKeyWord(appKeyword, GetByInstallDate)(contextSupport)\n          .value\n          .run\n        result must beLike {\n          case Right(iter) =>\n            iter.moveToPosition(0) shouldEqual iterableCursorApps.moveToPosition(0)\n        }\n        there was one(mockPersistenceServices)\n          .fetchIterableAppsByKeyword(appKeyword, OrderByInstallDate, ascending = false)\n      }\n\n    \"get iterable apps ordered by category\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchIterableAppsByKeyword(any, any, any) returns TaskService(\n          Task(Either.right(iterableCursorApps)))\n        val result = deviceProcess\n          .getIterableAppsByKeyWord(appKeyword, GetByCategory)(contextSupport)\n          .value\n          .run\n        result must beLike {\n          case Right(iter) =>\n            iter.moveToPosition(0) shouldEqual iterableCursorApps.moveToPosition(0)\n        }\n        there was one(mockPersistenceServices)\n          .fetchIterableAppsByKeyword(appKeyword, OrderByCategory, ascending = true)\n      }\n\n    \"returns AppException if persistence service fails \" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchIterableAppsByKeyword(any, any, any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result =\n          deviceProcess.getIterableAppsByKeyWord(appKeyword, GetByName)(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n  }\n\n  \"Synchronize installed apps\" should {\n\n    \"gets and saves installed apps\" in\n      new DeviceProcessScope {\n\n        mockAppsServices.getInstalledApplications(any) returns TaskService(\n          Task(Either.right(seqApplicationData)))\n        mockPersistenceServices.fetchApps(any, any) returns TaskService.right(Seq.empty)\n        mockApiServices.googlePlayPackages(any)(any) returns TaskService(\n          Task(Either.right(Seq.empty)))\n        mockPersistenceServices.addApps(any) returns TaskService(\n          Task(Either.right(seqApplication.head)))\n\n        val result = deviceProcess.synchronizeInstalledApps(contextSupport).value.run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"don't call to api services if all applications are in the database\" in\n      new DeviceProcessScope {\n\n        mockAppsServices.getInstalledApplications(any) returns TaskService.right(\n          seqApplicationData)\n        mockPersistenceServices.fetchApps(any, any) returns TaskService.right(seqApplication)\n        mockPersistenceServices.deleteAppsByIds(any) returns TaskService.right(1)\n\n        val result = deviceProcess.synchronizeInstalledApps(contextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was no(mockApiServices).googlePlayPackages(any)(any)\n        there was no(mockPersistenceServices).addApps(any)\n      }\n\n    \"delete the duplicated apps\" in\n      new DeviceProcessScope {\n\n        val app1 = seqApplication.head\n          .copy(id = 1, packageName = applicationPackageName, className = applicationClassName)\n        val app2 = seqApplication.head\n          .copy(id = 2, packageName = applicationPackageName, className = applicationClassName)\n        val app3 = seqApplication.head.copy(\n          id = 3,\n          packageName = applicationPackageName + 3,\n          className = applicationClassName + 3)\n        val app4 = seqApplication.head\n          .copy(id = 4, packageName = applicationPackageName, className = applicationClassName)\n\n        mockAppsServices.getInstalledApplications(any) returns TaskService.right(\n          Seq(app1.toData, app2.toData, app3.toData))\n        mockPersistenceServices.fetchApps(any, any) returns TaskService.right(\n          Seq(app1, app2, app3, app4))\n        mockPersistenceServices.deleteAppsByIds(any) returns TaskService.right(1)\n\n        val result = deviceProcess.synchronizeInstalledApps(contextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockPersistenceServices).deleteAppsByIds(Seq(app4.id))\n        there was no(mockApiServices).googlePlayPackages(any)(any)\n        there was no(mockPersistenceServices).addApps(any)\n      }.pendingUntilFixed(\"Issue #943\")\n\n    \"call to api services only for those apps with different packageName\" in\n      new DeviceProcessScope {\n\n        val app1 = seqApplication.head\n          .copy(packageName = applicationPackageName, className = applicationClassName)\n        val app2 = seqApplication.head\n          .copy(packageName = applicationPackageName + 2, className = applicationClassName)\n\n        mockAppsServices.getInstalledApplications(any) returns TaskService.right(\n          Seq(app1.toData, app2.toData))\n        mockPersistenceServices.fetchApps(any, any) returns TaskService.right(Seq(app2))\n        mockPersistenceServices.deleteAppsByIds(any) returns TaskService.right(1)\n        mockApiServices.googlePlayPackages(any)(any) returns TaskService.right(\n          Seq(categorizedPackage))\n        mockPersistenceServices.addApps(any) returns TaskService.right(seqApplication.head)\n\n        val result = deviceProcess.synchronizeInstalledApps(contextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockApiServices).googlePlayPackages(===(Seq(app1.packageName)))(any)\n        there was one(mockPersistenceServices).addApps(Seq(app1.toData, app2.toData))\n      }.pendingUntilFixed(\"Issue #943\")\n\n    \"call to api services only for those apps with misc category and delete them from database\" in\n      new DeviceProcessScope {\n\n        val app1 =\n          seqApplication.head.copy(id = 1, packageName = applicationPackageName, category = Misc)\n        val app2 = seqApplication.head\n          .copy(id = 2, packageName = applicationPackageName + 2, category = Social)\n\n        mockAppsServices.getInstalledApplications(any) returns TaskService.right(\n          Seq(app1.toData, app2.toData))\n        mockPersistenceServices.fetchApps(any, any) returns TaskService.right(Seq(app1, app2))\n        mockApiServices.googlePlayPackages(any)(any) returns TaskService.right(\n          Seq(categorizedPackage))\n        mockPersistenceServices.deleteAppsByIds(any) returns TaskService.right(1)\n        mockPersistenceServices.addApps(any) returns TaskService.right(seqApplication.head)\n\n        val result = deviceProcess.synchronizeInstalledApps(contextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockApiServices).googlePlayPackages(===(Seq(app1.packageName)))(any)\n        there was one(mockPersistenceServices).deleteAppsByIds(Seq(app1.id))\n        there was one(mockPersistenceServices).addApps(Seq(app1.toData, app2.toData))\n      }.pendingUntilFixed(\"Issue #943\")\n\n    \"returns a AppException if persistence service fails\" in\n      new DeviceProcessScope {\n\n        mockAppsServices.getInstalledApplications(any) returns TaskService(\n          Task(Either.left(appInstalledException)))\n        mockAppsServices.getApplication(any)(any) returns TaskService(\n          Task(Either.left(appInstalledException)))\n        mockAppsServices.getDefaultApps(any) returns TaskService(\n          Task(Either.left(appInstalledException)))\n\n        val result = deviceProcess.synchronizeInstalledApps(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n    \"returns an empty Answer if api service fails\" in\n      new DeviceProcessScope {\n\n        mockAppsServices.getInstalledApplications(any) returns TaskService(\n          Task(Either.right(seqApplicationData)))\n        mockApiServices.googlePlayPackages(any)(any) returns TaskService(\n          Task(Either.left(apiServiceException)))\n        mockPersistenceServices.addApps(any) returns TaskService(\n          Task(Either.right(seqApplication.head)))\n        mockPersistenceServices.fetchApps(any, any) returns TaskService.right(Seq.empty)\n\n        val result = deviceProcess.synchronizeInstalledApps(contextSupport).value.run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"returns an AppException if persistence service fails\" in\n      new DeviceProcessScope {\n\n        mockAppsServices.getInstalledApplications(any) returns TaskService(\n          Task(Either.right(seqApplicationData)))\n        mockPersistenceServices.fetchApps(any, any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n\n        val result = deviceProcess.synchronizeInstalledApps(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n  }\n\n  \"Getting and saving an installed app\" should {\n\n    \"gets and saves an installed app\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.addApp(any) returns (TaskService(\n          Task(Either.right(seqApplication.head))),\n        TaskService(Task(Either.right(seqApplication(1)))),\n        TaskService(Task(Either.right(seqApplication(2)))))\n        mockAppsServices.getApplication(applicationPackageName)(contextSupport) returns TaskService(\n          Task(Either.right(seqApplicationData.head)))\n        mockApiServices.googlePlayPackage(any)(any) returns TaskService(\n          Task(Either.right(categorizedPackage)))\n\n        val result = deviceProcess.saveApp(applicationPackageName)(contextSupport).value.run\n        result shouldEqual Right(seqApplicationData.head)\n      }\n\n    \"returns an AppException if app service fails\" in\n      new DeviceProcessScope {\n\n        mockAppsServices.getInstalledApplications(contextSupport) returns TaskService(\n          Task(Either.left(appInstalledException)))\n        mockAppsServices.getApplication(applicationPackageName)(contextSupport) returns TaskService(\n          Task(Either.left(appInstalledException)))\n        mockAppsServices.getDefaultApps(contextSupport) returns TaskService(\n          Task(Either.left(appInstalledException)))\n\n        val result = deviceProcess.saveApp(applicationPackageName)(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n    \"returns an app with Misc category if api service fails\" in\n      new DeviceProcessScope {\n\n        val appsPersistenceFailed = seqApplication map (_.copy(category = Misc))\n        val appExpected           = seqApplicationData.head.copy(category = Misc)\n\n        mockAppsServices.getApplication(applicationPackageName)(contextSupport) returns TaskService(\n          Task(Either.right(seqApplicationData.head)))\n        mockApiServices.googlePlayPackage(any)(any) returns TaskService(\n          Task(Either.left(apiServiceException)))\n        mockPersistenceServices.addApp(any) returns (TaskService(\n          Task(Either.right(appsPersistenceFailed.head))),\n        TaskService(Task(Either.right(appsPersistenceFailed(1)))),\n        TaskService(Task(Either.right(appsPersistenceFailed(2)))))\n\n        val result = deviceProcess.saveApp(applicationPackageName)(contextSupport).value.run\n\n        result shouldEqual Right(appExpected)\n      }\n\n    \"returns an empty Answer if persistence service fails\" in\n      new DeviceProcessScope {\n\n        mockAppsServices.getApplication(applicationPackageName)(contextSupport) returns TaskService(\n          Task(Either.right(seqApplicationData.head)))\n        mockApiServices.googlePlayPackage(any)(any) returns TaskService(\n          Task(Either.right(categorizedPackage)))\n        mockPersistenceServices.addApp(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n\n        val result = deviceProcess.saveApp(applicationPackageName)(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n  }\n\n  \"Deleting an app\" should {\n\n    \"deletes an app\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.deleteAppByPackage(any) returns TaskService(\n          Task(Either.right(deletedApplication)))\n        val result = deviceProcess.deleteApp(applicationPackageName)(contextSupport).value.run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"returns an empty Answer if persistence service fails\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.deleteAppByPackage(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = deviceProcess.deleteApp(applicationPackageName)(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n  }\n\n  \"Updating an installed app\" should {\n\n    \"gets and saves one installed app\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.updateApp(any) returns TaskService(\n          Task(Either.right(updatedApplications)))\n        mockPersistenceServices.findAppByPackage(any) returns TaskService(\n          Task(Either.right(seqApplication.headOption)))\n        mockAppsServices.getApplication(applicationPackageName)(contextSupport) returns TaskService(\n          Task(Either.right(seqApplicationData.head)))\n        mockApiServices.googlePlayPackage(any)(any) returns TaskService(\n          Task(Either.right(categorizedPackage)))\n\n        val result = deviceProcess.updateApp(applicationPackageName)(contextSupport).value.run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"returns an empty Answer if api service fails\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.updateApp(any) returns TaskService(\n          Task(Either.right(updatedApplications)))\n        mockAppsServices.getApplication(applicationPackageName)(contextSupport) returns TaskService(\n          Task(Either.right(seqApplicationData.head)))\n        mockPersistenceServices.findAppByPackage(any) returns TaskService(\n          Task(Either.right(seqApplication.headOption)))\n        mockApiServices.googlePlayPackage(any)(any) returns TaskService(\n          Task(Either.left(apiServiceException)))\n\n        val result = deviceProcess.updateApp(applicationPackageName)(contextSupport).value.run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"returns an AppException if persistence service fails\" in\n      new DeviceProcessScope {\n\n        mockAppsServices.getApplication(applicationPackageName)(contextSupport) returns TaskService(\n          Task(Either.right(seqApplicationData.head)))\n        mockPersistenceServices.findAppByPackage(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n\n        val result = deviceProcess.updateApp(applicationPackageName)(contextSupport).value.run\n        result must beAnInstanceOf[Left[AppException, _]]\n      }\n\n  }\n\n  \"Get Widgets\" should {\n\n    \"get widgets\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchApps(any, any) returns TaskService(\n          Task(Either.right(seqApplication)))\n        mockWidgetsServices.getWidgets(any) returns\n          TaskService(Task(Either.right(seqAppWidget.zipWithIndex.map {\n            case (appWidget, index) =>\n              appWidget.copy(\n                packageName = applicationPackageName + index,\n                className = applicationClassName + index)\n          })))\n\n        val result = deviceProcess.getWidgets(contextSupport).value.run\n        result shouldEqual Right(seqAppsWithWidgets)\n      }.pendingUntilFixed(\"Issue #943\")\n\n    \"returns WidgetException if WidgetServices fail getting the Widgets \" in\n      new DeviceProcessScope {\n\n        mockWidgetsServices.getWidgets(any) returns TaskService {\n          Task(Either.left(widgetsServicesException))\n        }\n        val result = deviceProcess.getWidgets(contextSupport).value.run\n        result must beAnInstanceOf[Left[WidgetException, _]]\n      }\n\n  }\n\n  \"Get Last Calls\" should {\n\n    \"get last calls\" in\n      new DeviceProcessScope {\n\n        mockCallsServices.getLastCalls returns TaskService(Task(Either.right(seqCall)))\n        mockContactsServices.fetchContactByPhoneNumber(call.number) returns TaskService(\n          Task(Either.right(Some(contact))))\n        mockContactsServices.fetchContactByPhoneNumber(seqCall(1).number) returns TaskService(\n          Task(Either.right(Some(seqContact(1)))))\n        mockContactsServices.fetchContactByPhoneNumber(seqCall(2).number) returns TaskService(\n          Task(Either.right(Some(seqContact(2)))))\n\n        val result = deviceProcess.getLastCalls(contextSupport).value.run\n        result shouldEqual Right(seqLastCallsContactByDate)\n      }\n\n    \"returns CallsException if CallsServices fail getting the calls \" in\n      new DeviceProcessScope {\n\n        mockCallsServices.getLastCalls returns TaskService(\n          Task(Either.left(callsServicesException)))\n        val result = deviceProcess.getLastCalls(contextSupport).value.run\n        result must beAnInstanceOf[Left[CallException, _]]\n      }\n\n    \"returns an empty List if ContactsServices fail getting the contacts \" in\n      new DeviceProcessScope {\n\n        mockCallsServices.getLastCalls returns TaskService(Task(Either.right(seqCall)))\n        mockContactsServices.fetchContactByPhoneNumber(any) returns TaskService(\n          Task(Either.left(contactsServicesException)))\n\n        val result = deviceProcess.getLastCalls(contextSupport).value.run\n        result shouldEqual Right(Seq())\n      }\n\n  }\n\n  \"Generate Dock Apps\" should {\n\n    \"returns a empty answer for a valid request\" in\n      new DeviceProcessScope {\n\n        mockAppsServices.getDefaultApps(contextSupport) returns TaskService(\n          Task(Either.right(seqApplicationData)))\n        mockPersistenceServices.createOrUpdateDockApp(any) returns TaskService(\n          Task(Either.right(seqDockApp)))\n        mockPersistenceServices.fetchAppByPackages(any) returns TaskService(\n          Task(Either.right(seqApplication)))\n        mockPersistenceServices.findAppByPackage(any) returns TaskService(\n          Task(Either.right(seqApplication.headOption)))\n\n        val result = deviceProcess.generateDockApps(dockAppSize)(contextSupport).value.run\n        result shouldEqual Right(seqDockApp)\n      }\n\n    \"returns DockAppException when AppService fails\" in\n      new DeviceProcessScope {\n\n        mockAppsServices.getDefaultApps(contextSupport) returns TaskService(\n          Task(Either.left(appInstalledException)))\n        val result = deviceProcess.generateDockApps(dockAppSize)(contextSupport).value.run\n        result must beAnInstanceOf[Left[DockAppException, _]]\n      }\n\n    \"returns DockAppException when PersistenceService fails fetching the apps\" in\n      new DeviceProcessScope {\n\n        mockAppsServices.getDefaultApps(contextSupport) returns TaskService(\n          Task(Either.right(seqApplicationData)))\n        mockPersistenceServices.createOrUpdateDockApp(any) returns TaskService(\n          Task(Either.right(seqDockApp)))\n        mockPersistenceServices.fetchAppByPackages(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n\n        val result = deviceProcess.generateDockApps(dockAppSize)(contextSupport).value.run\n        result must beAnInstanceOf[Left[DockAppException, _]]\n      }\n\n    \"returns DockAppException when PersistenceService fails saving the apps\" in\n      new DeviceProcessScope {\n\n        mockAppsServices.getDefaultApps(contextSupport) returns TaskService(\n          Task(Either.right(seqApplicationData)))\n        mockPersistenceServices.fetchAppByPackages(any) returns TaskService(\n          Task(Either.right(seqApplication)))\n        mockPersistenceServices.createOrUpdateDockApp(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n\n        val result = deviceProcess.generateDockApps(dockAppSize)(contextSupport).value.run\n        result must beAnInstanceOf[Left[DockAppException, _]]\n      }\n  }\n\n  \"Create Or Update Dock App\" should {\n\n    \"returns a empty answer for a valid request\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.createOrUpdateDockApp(any) returns TaskService(\n          Task(Either.right(seqDockApp)))\n        val result = deviceProcess\n          .createOrUpdateDockApp(\n            dockAppName,\n            AppDockType,\n            jsonToNineCardIntent(intent),\n            dockAppImagePath,\n            0)\n          .value\n          .run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"returns DockAppException when PersistenceService fails\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.createOrUpdateDockApp(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = deviceProcess\n          .createOrUpdateDockApp(\n            dockAppName,\n            AppDockType,\n            jsonToNineCardIntent(intent),\n            dockAppImagePath,\n            0)\n          .value\n          .run\n        result must beAnInstanceOf[Left[DockAppException, _]]\n      }\n\n  }\n\n  \"Save DockApps\" should {\n\n    \"return the three dockApps saved\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.createOrUpdateDockApp(any) returns TaskService(\n          Task(Either.right(seqDockApp)))\n        val result = deviceProcess.saveDockApps(seqDockAppData).value.run\n        result must beLike {\n          case Right(resultSeqDockApp) =>\n            resultSeqDockApp.size shouldEqual seqDockAppData.size\n        }\n      }\n\n    \"returns DockAppException when PersistenceService fails\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.createOrUpdateDockApp(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = deviceProcess.saveDockApps(seqDockAppData).value.run\n        result must beAnInstanceOf[Left[DockAppException, _]]\n      }\n\n  }\n\n  \"Get Dock Apps\" should {\n\n    \"get dock apps stored\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchDockApps returns TaskService(Task(Either.right(seqDockApp)))\n        val result = deviceProcess.getDockApps.value.run\n        result must beLike {\n          case Right(resultDockApp) =>\n            resultDockApp map (_.name) shouldEqual (seqDockApp map (_.name))\n        }\n      }\n\n    \"returns DockAppException when PersistenceService fails\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.fetchDockApps returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = deviceProcess.getDockApps.value.run\n        result must beAnInstanceOf[Left[DockAppException, _]]\n      }\n  }\n\n  \"Delete All Dock Apps\" should {\n\n    \"returns a empty answer for a valid request\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.deleteAllDockApps() returns TaskService(\n          Task(Either.right(deletedDockApps)))\n        val result = deviceProcess.deleteAllDockApps().value.run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"returns DockAppException when PersistenceService fails\" in\n      new DeviceProcessScope {\n\n        mockPersistenceServices.deleteAllDockApps() returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = deviceProcess.deleteAllDockApps().value.run\n        result must beAnInstanceOf[Left[DockAppException, _]]\n      }\n  }\n\n  \"getConfiguredNetworks\" should {\n\n    \"returns all networks for a valid request\" in\n      new DeviceProcessScope {\n\n        mockConnectivityServices.getConfiguredNetworks(contextSupport) returns TaskService(\n          Task(Either.right(networks)))\n        val result = deviceProcess.getConfiguredNetworks(contextSupport).value.run\n        result shouldEqual Right(networks)\n      }\n  }\n\n  \"getPairedDevices\" should {\n\n    \"returns all paired bluetooth devices for a valid request\" in\n      new DeviceProcessScope {\n        val names = bluetoothDevices map (_.name)\n\n        mockConnectivityServices.getPairedDevices returns TaskService(\n          Task(Either.right(bluetoothDevices)))\n        val result = deviceProcess.getPairedBluetoothDevices(contextSupport).value.run\n        result shouldEqual Right(names)\n      }\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/intents/impl/LauncherExecutorProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.intents.impl\n\nimport android.content.Intent\nimport cards.nine.commons.contexts.ActivityContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.LauncherExecutorTestData\nimport cards.nine.commons.test.data.LauncherExecutorValues._\nimport cards.nine.models._\nimport cards.nine.process.intents.{\n  LauncherExecutorProcessException,\n  LauncherExecutorProcessPermissionException\n}\nimport cards.nine.services.intents.{\n  IntentLauncherServicesException,\n  IntentLauncherServicesPermissionException,\n  LauncherIntentServices\n}\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait LauncherExecutorProcessImplSpecification\n    extends Specification\n    with Mockito\n    with LauncherExecutorTestData {\n\n  val intentLauncherServicesException = IntentLauncherServicesException(exceptionMessage)\n  val intentLauncherServicesPermissionException = IntentLauncherServicesPermissionException(\n    exceptionMessage)\n\n  val serviceRight: TaskService[Unit] =\n    TaskService(Task(Either.right((): Unit)))\n  val serviceException: TaskService[Unit] =\n    TaskService(Task(Either.left(intentLauncherServicesException)))\n  val servicePermissionException: TaskService[Unit] =\n    TaskService(Task(Either.left(intentLauncherServicesPermissionException)))\n\n  trait LauncherExecutorProcessImplScope extends Scope {\n\n    val mockActivityContext = mock[ActivityContextSupport]\n\n    val mockServices = mock[LauncherIntentServices]\n\n    val mockIntent = mock[NineCardsIntent]\n\n    val process = new LauncherExecutorProcessImpl(config, mockServices)\n\n    def verifyRight(\n        processService: (ActivityContextSupport) => TaskService[Unit],\n        action: IntentAction): Unit = {\n      mockServices.launchIntentAction(any)(any) returns serviceRight\n\n      val result = processService(mockActivityContext).value.run\n      result shouldEqual Right((): Unit)\n\n      there was one(mockServices).launchIntentAction(action)(mockActivityContext)\n    }\n\n    def verifyLeftPermission(\n        processService: (ActivityContextSupport) => TaskService[Unit],\n        action: IntentAction): Unit = {\n      mockServices.launchIntentAction(any)(any) returns servicePermissionException\n\n      val result = processService(mockActivityContext).value.run\n      result must beAnInstanceOf[Left[LauncherExecutorProcessPermissionException, _]]\n\n      there was one(mockServices).launchIntentAction(action)(mockActivityContext)\n    }\n\n    def verifyLeft(\n        processService: (ActivityContextSupport) => TaskService[Unit],\n        action: IntentAction): Unit = {\n      mockServices.launchIntentAction(any)(any) returns serviceException\n\n      val result = processService(mockActivityContext).value.run\n      result must beAnInstanceOf[Left[LauncherExecutorProcessException, _]]\n\n      there was one(mockServices).launchIntentAction(action)(mockActivityContext)\n    }\n\n  }\n\n  trait WithAppIntent { self: LauncherExecutorProcessImplScope =>\n\n    mockIntent.getAction returns NineCardsIntentExtras.openApp\n    mockIntent.extractPackageName() returns Some(launcherExecutorPackageName)\n    mockIntent.extractClassName() returns Some(launcherExecutorClassName)\n\n  }\n\n}\n\nclass LauncherExecutorProcessImplSpec extends LauncherExecutorProcessImplSpecification {\n\n  \"execute for openApp\" should {\n\n    \"call to the service with the application action for an openApp intent with package and class name\" in\n      new LauncherExecutorProcessImplScope with WithAppIntent {\n        mockServices.launchIntentAction(any)(any) returns serviceRight\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockServices).launchIntentAction(appAction)(mockActivityContext)\n        there was no(mockServices).launchIntentAction(appLauncherAction)(mockActivityContext)\n        there was no(mockServices).launchIntentAction(appGooglePlayAction)(mockActivityContext)\n      }\n\n    \"call to the service with the launch application action for an openApp intent when the application action \" +\n      \"returns an Exception\" in\n      new LauncherExecutorProcessImplScope with WithAppIntent {\n        mockServices.launchIntentAction(===(appAction))(any) returns serviceException\n        mockServices.launchIntentAction(===(appLauncherAction))(any) returns serviceRight\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockServices).launchIntentAction(appAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(appLauncherAction)(mockActivityContext)\n        there was no(mockServices).launchIntentAction(appGooglePlayAction)(mockActivityContext)\n      }\n\n    \"call to the service with the play store action for an openApp intent when both the application action and \" +\n      \"the application launch action returns an Exception\" in\n      new LauncherExecutorProcessImplScope with WithAppIntent {\n        mockServices.launchIntentAction(===(appAction))(any) returns serviceException\n        mockServices.launchIntentAction(===(appLauncherAction))(any) returns serviceException\n        mockServices.launchIntentAction(===(appGooglePlayAction))(any) returns serviceRight\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockServices).launchIntentAction(appAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(appLauncherAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(appGooglePlayAction)(mockActivityContext)\n      }\n\n    \"call to the service with the launch application action for an openApp intent with package but no class name\" in\n      new LauncherExecutorProcessImplScope {\n        mockServices.launchIntentAction(any)(any) returns serviceRight\n\n        mockIntent.getAction returns NineCardsIntentExtras.openApp\n        mockIntent.extractPackageName() returns Some(launcherExecutorPackageName)\n        mockIntent.extractClassName() returns None\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result shouldEqual Right((): Unit)\n\n        there was no(mockServices).launchIntentAction(appAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(appLauncherAction)(mockActivityContext)\n        there was no(mockServices).launchIntentAction(appGooglePlayAction)(mockActivityContext)\n      }\n\n    \"returns a Left[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception \" +\n      \"for the application action\" in\n      new LauncherExecutorProcessImplScope with WithAppIntent {\n        mockServices.launchIntentAction(===(appAction))(any) returns servicePermissionException\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessPermissionException, _]]\n\n        there was one(mockServices).launchIntentAction(appAction)(mockActivityContext)\n        there was no(mockServices).launchIntentAction(appLauncherAction)(mockActivityContext)\n        there was no(mockServices).launchIntentAction(appGooglePlayAction)(mockActivityContext)\n      }\n\n    \"returns a Left[LauncherExecutorProcessPermissionException, _] if the service returns an exception for \" +\n      \"the application action and a Permission exception for the application launch action\" in\n      new LauncherExecutorProcessImplScope with WithAppIntent {\n        mockServices.launchIntentAction(===(appAction))(any) returns serviceException\n        mockServices.launchIntentAction(===(appLauncherAction))(any) returns servicePermissionException\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessPermissionException, _]]\n\n        there was one(mockServices).launchIntentAction(appAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(appLauncherAction)(mockActivityContext)\n        there was no(mockServices).launchIntentAction(appGooglePlayAction)(mockActivityContext)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns an exception \" +\n      \"for both the application action and the application launch action but a Permission exception \" +\n      \"for google play store action\" in\n      new LauncherExecutorProcessImplScope with WithAppIntent {\n        mockServices.launchIntentAction(===(appAction))(any) returns serviceException\n        mockServices.launchIntentAction(===(appLauncherAction))(any) returns serviceException\n        mockServices.launchIntentAction(===(appGooglePlayAction))(any) returns servicePermissionException\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessPermissionException, _]]\n\n        there was one(mockServices).launchIntentAction(appAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(appLauncherAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(appGooglePlayAction)(mockActivityContext)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception for all actions\" in\n      new LauncherExecutorProcessImplScope with WithAppIntent {\n        mockServices.launchIntentAction(any)(any) returns serviceException\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessException, _]]\n\n        there was one(mockServices).launchIntentAction(appAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(appLauncherAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(appGooglePlayAction)(mockActivityContext)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the intent doesn't have a package name\" in\n      new LauncherExecutorProcessImplScope {\n\n        mockIntent.getAction returns NineCardsIntentExtras.openApp\n        mockIntent.extractPackageName() returns None\n        mockIntent.extractClassName() returns None\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessException, _]]\n\n        there was no(mockServices).launchIntentAction(appAction)(mockActivityContext)\n        there was no(mockServices).launchIntentAction(appLauncherAction)(mockActivityContext)\n        there was no(mockServices).launchIntentAction(appGooglePlayAction)(mockActivityContext)\n      }\n\n  }\n\n  \"execute for openNoInstalledApp\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openNoInstalledApp\n        mockIntent.extractPackageName() returns Some(launcherExecutorPackageName)\n\n        verifyRight(process.execute(mockIntent)(_), appGooglePlayAction)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openNoInstalledApp\n        mockIntent.extractPackageName() returns Some(launcherExecutorPackageName)\n\n        verifyLeftPermission(process.execute(mockIntent)(_), appGooglePlayAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openNoInstalledApp\n        mockIntent.extractPackageName() returns Some(launcherExecutorPackageName)\n\n        verifyLeft(process.execute(mockIntent)(_), appGooglePlayAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the intent doesn't have a package name\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openNoInstalledApp\n        mockIntent.extractPackageName() returns None\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessException, _]]\n\n        there was no(mockServices).launchIntentAction(appGooglePlayAction)(mockActivityContext)\n      }\n\n  }\n\n  \"execute for openSms\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openSms\n        mockIntent.extractPhone() returns Some(launcherExecutorPhoneNumber)\n\n        verifyRight(process.execute(mockIntent)(_), phoneSmsAction)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openSms\n        mockIntent.extractPhone() returns Some(launcherExecutorPhoneNumber)\n\n        verifyLeftPermission(process.execute(mockIntent)(_), phoneSmsAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openSms\n        mockIntent.extractPhone() returns Some(launcherExecutorPhoneNumber)\n\n        verifyLeft(process.execute(mockIntent)(_), phoneSmsAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the intent doesn't have a phone number\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openSms\n        mockIntent.extractPhone() returns None\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessException, _]]\n\n        there was no(mockServices).launchIntentAction(phoneSmsAction)(mockActivityContext)\n      }\n\n  }\n\n  \"execute for openPhone\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openPhone\n        mockIntent.extractPhone() returns Some(launcherExecutorPhoneNumber)\n\n        verifyRight(process.execute(mockIntent)(_), phoneCallAction)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openPhone\n        mockIntent.extractPhone() returns Some(launcherExecutorPhoneNumber)\n\n        verifyLeftPermission(process.execute(mockIntent)(_), phoneCallAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openPhone\n        mockIntent.extractPhone() returns Some(launcherExecutorPhoneNumber)\n\n        verifyLeft(process.execute(mockIntent)(_), phoneCallAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the intent doesn't have a phone number\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openPhone\n        mockIntent.extractPhone() returns None\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessException, _]]\n\n        there was no(mockServices).launchIntentAction(phoneCallAction)(mockActivityContext)\n      }\n\n  }\n\n  \"execute for openEmail\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openEmail\n        mockIntent.extractEmail() returns Some(launcherExecutorEmail)\n\n        verifyRight(process.execute(mockIntent)(_), emailAction)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openEmail\n        mockIntent.extractEmail() returns Some(launcherExecutorEmail)\n\n        verifyLeftPermission(process.execute(mockIntent)(_), emailAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openEmail\n        mockIntent.extractEmail() returns Some(launcherExecutorEmail)\n\n        verifyLeft(process.execute(mockIntent)(_), emailAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the intent doesn't have an email\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openEmail\n        mockIntent.extractEmail() returns None\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessException, _]]\n\n        there was no(mockServices).launchIntentAction(emailAction)(mockActivityContext)\n      }\n\n  }\n\n  \"execute for openContact\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openContact\n        mockIntent.extractLookup() returns Some(launcherExecutorLookupKey)\n\n        verifyRight(process.execute(mockIntent)(_), contactAction)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openContact\n        mockIntent.extractLookup() returns Some(launcherExecutorLookupKey)\n\n        verifyLeftPermission(process.execute(mockIntent)(_), contactAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openContact\n        mockIntent.extractLookup() returns Some(launcherExecutorLookupKey)\n\n        verifyLeft(process.execute(mockIntent)(_), contactAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the intent doesn't have a contact lookup\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns NineCardsIntentExtras.openContact\n        mockIntent.extractLookup() returns None\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessException, _]]\n\n        there was no(mockServices).launchIntentAction(contactAction)(mockActivityContext)\n      }\n\n  }\n\n  \"execute for an unknown action\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns unknownAction\n        val androidIntent = mock[Intent]\n        mockIntent.toIntent returns androidIntent\n        mockServices.launchIntent(any)(any) returns serviceRight\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockServices).launchIntent(androidIntent)(mockActivityContext)\n\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns unknownAction\n        val androidIntent = mock[Intent]\n        mockIntent.toIntent returns androidIntent\n        mockServices.launchIntent(any)(any) returns servicePermissionException\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessPermissionException, _]]\n\n        there was one(mockServices).launchIntent(androidIntent)(mockActivityContext)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        mockIntent.getAction returns unknownAction\n        val androidIntent = mock[Intent]\n        mockIntent.toIntent returns androidIntent\n        mockServices.launchIntent(any)(any) returns serviceException\n\n        val result = process.execute(mockIntent)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessException, _]]\n\n        there was one(mockServices).launchIntent(androidIntent)(mockActivityContext)\n      }\n\n  }\n\n  \"executeContact\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        verifyRight(process.executeContact(launcherExecutorLookupKey)(_), contactAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeftPermission(process.executeContact(launcherExecutorLookupKey)(_), contactAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeft(process.executeContact(launcherExecutorLookupKey)(_), contactAction)\n      }\n\n  }\n\n  \"launchShare\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        verifyRight(process.launchShare(shareText)(_), shareAction)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeftPermission(process.launchShare(shareText)(_), shareAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeft(process.launchShare(shareText)(_), shareAction)\n      }\n\n  }\n\n  \"launchSearch\" should {\n\n    \"call to the services with the global search action parameter\" in\n      new LauncherExecutorProcessImplScope {\n        verifyRight(process.launchSearch(_), searchGlobalAction)\n      }\n\n    \"call to the services with the web search action if the service returns an exception \" +\n      \"for the global search action\" in\n      new LauncherExecutorProcessImplScope {\n        mockServices.launchIntentAction(anyOf(SearchGlobalAction))(any) returns serviceException\n        mockServices.launchIntentAction(anyOf(SearchWebAction))(any) returns serviceRight\n\n        val result = process.launchSearch(mockActivityContext).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockServices).launchIntentAction(searchGlobalAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(searchWebAction)(mockActivityContext)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception \" +\n      \"for the global search action\" in\n      new LauncherExecutorProcessImplScope {\n        mockServices.launchIntentAction(anyOf(SearchGlobalAction))(any) returns servicePermissionException\n\n        val result = process.launchSearch(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessPermissionException, _]]\n\n        there was one(mockServices).launchIntentAction(searchGlobalAction)(mockActivityContext)\n        there was no(mockServices).launchIntentAction(searchWebAction)(mockActivityContext)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns an exception for \" +\n      \"the global search action and permission exception for the web search\" in\n      new LauncherExecutorProcessImplScope {\n        mockServices.launchIntentAction(anyOf(SearchGlobalAction))(any) returns serviceException\n        mockServices.launchIntentAction(anyOf(SearchWebAction))(any) returns servicePermissionException\n\n        val result = process.launchSearch(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessPermissionException, _]]\n\n        there was one(mockServices).launchIntentAction(searchGlobalAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(searchWebAction)(mockActivityContext)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception \" +\n      \"for both search actions\" in\n      new LauncherExecutorProcessImplScope {\n        mockServices.launchIntentAction(anyOf(SearchGlobalAction))(any) returns serviceException\n        mockServices.launchIntentAction(anyOf(SearchWebAction))(any) returns serviceException\n\n        val result = process.launchSearch(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessException, _]]\n\n        there was one(mockServices).launchIntentAction(searchGlobalAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(searchWebAction)(mockActivityContext)\n      }\n\n  }\n\n  \"launchGoogleWeather\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        verifyRight(process.launchGoogleWeather(_), googleWeatherAction)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeftPermission(process.launchGoogleWeather(_), googleWeatherAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeft(process.launchGoogleWeather(_), googleWeatherAction)\n      }\n\n  }\n\n  \"launchVoiceSearch\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        verifyRight(process.launchVoiceSearch(_), searchVoiceAction)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeftPermission(process.launchVoiceSearch(_), searchVoiceAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeft(process.launchVoiceSearch(_), searchVoiceAction)\n      }\n\n  }\n\n  \"launchSettings\" should {\n\n    \"call to the services with the app settings action parameter\" in\n      new LauncherExecutorProcessImplScope {\n        verifyRight(process.launchSettings(launcherExecutorPackageName)(_), appSettingsAction)\n      }\n\n    \"call to the services with the global settings action if the service returns an exception \" +\n      \"for the app settings action\" in\n      new LauncherExecutorProcessImplScope {\n        mockServices.launchIntentAction(any[AppSettingsAction])(any) returns serviceException\n        mockServices.launchIntentAction(anyOf(GlobalSettingsAction))(any) returns serviceRight\n\n        val result =\n          process.launchSettings(launcherExecutorPackageName)(mockActivityContext).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockServices).launchIntentAction(appSettingsAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(globalSettingsAction)(mockActivityContext)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception \" +\n      \"for the app settings action\" in\n      new LauncherExecutorProcessImplScope {\n        mockServices.launchIntentAction(any[AppSettingsAction])(any) returns servicePermissionException\n\n        val result =\n          process.launchSettings(launcherExecutorPackageName)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessPermissionException, _]]\n\n        there was one(mockServices).launchIntentAction(appSettingsAction)(mockActivityContext)\n        there was no(mockServices).launchIntentAction(globalSettingsAction)(mockActivityContext)\n      }\n\n    \"returns a Left[LauncherExecutorProcessPermissionException, _] if the service returns an exception for \" +\n      \"the app settings action and permission exception for the global settings search\" in\n      new LauncherExecutorProcessImplScope {\n        mockServices.launchIntentAction(any[AppSettingsAction])(any) returns serviceException\n        mockServices.launchIntentAction(anyOf(GlobalSettingsAction))(any) returns servicePermissionException\n\n        val result =\n          process.launchSettings(launcherExecutorPackageName)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessPermissionException, _]]\n\n        there was one(mockServices).launchIntentAction(appSettingsAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(globalSettingsAction)(mockActivityContext)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception \" +\n      \"for both search actions\" in\n      new LauncherExecutorProcessImplScope {\n        mockServices.launchIntentAction(any[AppSettingsAction])(any) returns serviceException\n        mockServices.launchIntentAction(anyOf(GlobalSettingsAction))(any) returns serviceException\n\n        val result =\n          process.launchSettings(launcherExecutorPackageName)(mockActivityContext).value.run\n        result must beAnInstanceOf[Left[LauncherExecutorProcessException, _]]\n\n        there was one(mockServices).launchIntentAction(appSettingsAction)(mockActivityContext)\n        there was one(mockServices).launchIntentAction(globalSettingsAction)(mockActivityContext)\n      }\n\n  }\n\n  \"launchUninstall\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        verifyRight(process.launchUninstall(launcherExecutorPackageName)(_), appUninstallAction)\n      }\n\n    \"returns a eft[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeftPermission(\n          process.launchUninstall(launcherExecutorPackageName)(_),\n          appUninstallAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeft(process.launchUninstall(launcherExecutorPackageName)(_), appUninstallAction)\n      }\n\n  }\n\n  \"launchDial\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        verifyRight(process.launchDial(Some(launcherExecutorPhoneNumber))(_), phoneDialAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeftPermission(\n          process.launchDial(Some(launcherExecutorPhoneNumber))(_),\n          phoneDialAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeft(process.launchDial(Some(launcherExecutorPhoneNumber))(_), phoneDialAction)\n      }\n\n  }\n\n  \"launchPlayStore\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        verifyRight(process.launchPlayStore(_), googlePlayStoreAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeftPermission(process.launchPlayStore(_), googlePlayStoreAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeft(process.launchPlayStore(_), googlePlayStoreAction)\n      }\n\n  }\n\n  \"launchApp\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        verifyRight(process.launchApp(launcherExecutorPackageName)(_), appLauncherAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeftPermission(process.launchApp(launcherExecutorPackageName)(_), appLauncherAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeft(process.launchApp(launcherExecutorPackageName)(_), appLauncherAction)\n      }\n\n  }\n\n  \"launchGooglePlay\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        verifyRight(process.launchGooglePlay(launcherExecutorPackageName)(_), appGooglePlayAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeftPermission(\n          process.launchGooglePlay(launcherExecutorPackageName)(_),\n          appGooglePlayAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeft(process.launchGooglePlay(launcherExecutorPackageName)(_), appGooglePlayAction)\n      }\n\n  }\n\n  \"launchUrl\" should {\n\n    \"call to the services with the right parameters\" in\n      new LauncherExecutorProcessImplScope {\n        verifyRight(process.launchUrl(launcherExecutorUrl)(_), urlAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessPermissionException, _] if the service returns a Permission exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeftPermission(process.launchUrl(launcherExecutorUrl)(_), urlAction)\n      }\n\n    \"returns a Left[LauncherExecutorProcessException, _] if the service returns an exception\" in\n      new LauncherExecutorProcessImplScope {\n        verifyLeft(process.launchUrl(launcherExecutorUrl)(_), urlAction)\n      }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/moment/impl/MomentProcessImplData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.moment.impl\n\nimport cards.nine.commons.test.data.MomentValues._\nimport cards.nine.models._\nimport cards.nine.models.types.CardType._\nimport cards.nine.models.types.CollectionType._\nimport cards.nine.models.types.NineCardsCategory._\nimport cards.nine.models.types._\nimport org.joda.time.DateTime\n\nimport scala.util.Random\n\ntrait MomentProcessImplData {\n\n  val collectionId                        = Random.nextInt(10)\n  val name: String                        = Random.nextString(5)\n  val collectionType: CollectionType      = collectionTypes(Random.nextInt(collectionTypes.length))\n  val icon: String                        = Random.nextString(5)\n  val themedColorIndex: Int               = Random.nextInt(10)\n  val appsCategory: NineCardsCategory     = appsCategories(Random.nextInt(appsCategories.length))\n  val originalSharedCollectionId: String  = Random.nextString(5)\n  val sharedCollectionId: String          = Random.nextString(5)\n  val sharedCollectionSubscribed: Boolean = Random.nextBoolean()\n  val publicCollectionStatusSeq           = Seq(NotPublished, PublishedByMe, PublishedByOther)\n  val publicCollectionStatus = publicCollectionStatusSeq(\n    Random.nextInt(publicCollectionStatusSeq.size))\n\n  val name1                    = \"Scala Android\"\n  val packageName1             = \"com.fortysevendeg.scala.android\"\n  val className1               = \"ScalaAndroidActivity\"\n  val category1                = \"Communication\"\n  val imagePath1               = \"imagePath1\"\n  val dateInstalled1           = 1L\n  val dateUpdate1              = 1L\n  val version1                 = \"22\"\n  val installedFromGooglePlay1 = true\n\n  val appId                = Random.nextInt(10)\n  val momentId             = Random.nextInt(10)\n  val cardId               = Random.nextInt(10)\n  val position: Int        = Random.nextInt(10)\n  val term: String         = Random.nextString(5)\n  val packageName          = Random.nextString(5)\n  val className            = Random.nextString(5)\n  val cardType: CardType   = cardTypes(Random.nextInt(cardTypes.length))\n  val imagePath: String    = Random.nextString(5)\n  val notification: String = Random.nextString(5)\n  val intent =\n    \"\"\"{ \"className\": \"classNameValue\", \"packageName\": \"packageNameValue\", \"categories\": [\"Communication\"], \"action\": \"actionValue\", \"extras\": { \"pairValue\": \"pairValue\", \"empty\": false, \"parcelled\": false }, \"flags\": 1, \"type\": \"typeValue\"}\"\"\"\n\n  val from = \"8:00\"\n  val to   = \"19:00\"\n  val days = Seq(0, 1, 1, 1, 1, 1, 0)\n\n  val collectionId1         = 1\n  val homeAppPackageName    = \"com.google.android.apps.plus\"\n  val nightAppPackageName   = \"com.Slack\"\n  val workAppPackageName    = \"com.google.android.apps.photos\"\n  val transitAppPackageName = \"com.google.android.apps.maps\"\n\n  val startX: Int = Random.nextInt(8)\n  val startY: Int = Random.nextInt(8)\n  val spanX: Int  = Random.nextInt(8)\n  val spanY: Int  = Random.nextInt(8)\n\n  val ssid: String = Random.nextString(5)\n\n  val item: Int = Random.nextInt(5)\n\n  val homeApp =\n    ApplicationData(\n      name = name,\n      packageName = homeAppPackageName,\n      className = className1,\n      category = NineCardsCategory(category1),\n      dateInstalled = dateInstalled1,\n      dateUpdated = dateUpdate1,\n      version = version1,\n      installedFromGooglePlay = installedFromGooglePlay1)\n\n  val workApp = homeApp.copy(packageName = workAppPackageName)\n\n  val nightApp = homeApp.copy(packageName = nightAppPackageName)\n\n  val transitApp = homeApp.copy(packageName = transitAppPackageName)\n\n  val now = DateTime.now()\n\n  val nowMorning        = now.withDayOfWeek(2).withTime(10, 0, 0, 0)\n  val nowAfternoon      = now.withDayOfWeek(2).withTime(18, 30, 0, 0)\n  val nowNight          = now.withDayOfWeek(2).withTime(21, 0, 0, 0)\n  val nowLateNight      = now.withDayOfWeek(2).withTime(3, 0, 0, 0)\n  val nowMorningWeekend = now.withDayOfWeek(7).withTime(10, 0, 0, 0)\n\n  val startHomeHour  = 8\n  val endHomeHour    = 19\n  val startWorkHour  = 8\n  val endWorkHour    = 17\n  val startNightHour = 20\n  val endNightHour   = 8\n  val startStudyHour = 8\n  val endStudyHour   = 18\n\n  def toSlotTime(start: Int, end: Int, days: Seq[Int]): Seq[MomentTimeSlot] = {\n    val startTime = if (start < 10) s\"0$start:00\" else s\"$start:00\"\n    val endTime   = if (end < 10) s\"0$end:00\" else s\"$end:00\"\n    if (start > end) {\n      Seq(\n        MomentTimeSlot(from = startTime, to = \"23:59\", days = days),\n        MomentTimeSlot(from = \"00:00\", to = endTime, days = days))\n    } else {\n      Seq(MomentTimeSlot(from = startTime, to = endTime, days = days))\n    }\n  }\n\n  val homeWifiSSID = \"homeSSID\"\n  val homeMoment = Moment(\n    id = momentId + 10,\n    collectionId = Some(momentCollectionId + 10),\n    timeslot = toSlotTime(startHomeHour, endHomeHour, days = Seq(1, 1, 1, 1, 1, 1, 1)),\n    wifi = Seq(homeWifiSSID),\n    bluetooth = Seq.empty,\n    headphone = false,\n    momentType = HomeMorningMoment,\n    widgets = None)\n\n  val workWifiSSID = \"workSSID\"\n  val workMoment = Moment(\n    id = momentId + 11,\n    collectionId = Some(momentCollectionId + 11),\n    timeslot = toSlotTime(startWorkHour, endWorkHour, days = Seq(0, 1, 1, 1, 1, 1, 0)),\n    wifi = Seq(workWifiSSID),\n    bluetooth = Seq.empty,\n    headphone = false,\n    momentType = WorkMoment,\n    widgets = None)\n\n  val nightMoment = Moment(\n    id = momentId + 12,\n    collectionId = Some(momentCollectionId + 12),\n    timeslot = toSlotTime(startNightHour, endNightHour, days = Seq(1, 1, 1, 1, 1, 1, 1)),\n    wifi = Seq.empty,\n    bluetooth = Seq.empty,\n    headphone = false,\n    momentType = HomeNightMoment,\n    widgets = None)\n\n  val studyMoment = Moment(\n    id = momentId + 13,\n    collectionId = Some(momentCollectionId + 13),\n    timeslot = toSlotTime(startStudyHour, endStudyHour, days = Seq(0, 1, 1, 1, 1, 1, 0)),\n    wifi = Seq.empty,\n    bluetooth = Seq.empty,\n    headphone = false,\n    momentType = StudyMoment,\n    widgets = None)\n\n  val musicMoment = Moment(\n    id = momentId + 14,\n    collectionId = Some(momentCollectionId + 14),\n    timeslot = Seq.empty,\n    wifi = Seq.empty,\n    bluetooth = Seq.empty,\n    headphone = true,\n    momentType = MusicMoment,\n    widgets = None)\n\n  val carMoment = Moment(\n    id = momentId + 15,\n    collectionId = Some(momentCollectionId + 15),\n    timeslot = Seq.empty,\n    wifi = Seq.empty,\n    bluetooth = Seq.empty,\n    headphone = false,\n    momentType = CarMoment,\n    widgets = None)\n\n  val sportsMoment = Moment(\n    id = momentId + 16,\n    collectionId = Some(momentCollectionId + 16),\n    timeslot = Seq.empty,\n    wifi = Seq.empty,\n    bluetooth = Seq.empty,\n    headphone = false,\n    momentType = SportMoment,\n    widgets = None)\n\n  val outAndAboutMoment = Moment(\n    id = momentId + 18,\n    collectionId = Some(momentCollectionId + 18),\n    timeslot = Seq.empty,\n    wifi = Seq.empty,\n    bluetooth = Seq.empty,\n    headphone = false,\n    momentType = OutAndAboutMoment,\n    widgets = None)\n\n  val allMoments = Seq(\n    homeMoment,\n    workMoment,\n    nightMoment,\n    studyMoment,\n    musicMoment,\n    sportsMoment,\n    carMoment,\n    outAndAboutMoment)\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/moment/impl/MomentProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.moment.impl\n\nimport android.content.Intent\nimport android.content.pm.PackageManager\nimport android.content.res.Resources\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.MomentValues._\nimport cards.nine.commons.test.data.{CollectionTestData, MomentTestData}\nimport cards.nine.models._\nimport cards.nine.models.types._\nimport cards.nine.process.moment.MomentException\nimport cards.nine.services.awareness.AwarenessServices\nimport cards.nine.services.persistence.{PersistenceServiceException, PersistenceServices}\nimport cards.nine.services.connectivity.{ConnectivityServices, WifiServicesException}\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.joda.time.DateTime\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait MomentProcessImplSpecification\n    extends TaskServiceSpecification\n    with MomentTestData\n    with CollectionTestData\n    with Mockito {\n\n  val persistenceServiceException = PersistenceServiceException(\"\")\n  val wifiServiceException        = WifiServicesException(\"\")\n\n  trait MomentProcessScope extends Scope with MomentProcessImplData with MomentTestData {\n\n    val resources = mock[Resources]\n\n    val contextSupport = mock[ContextSupport]\n    contextSupport.getPackageManager returns mock[PackageManager]\n    contextSupport.getResources returns resources\n\n    val mockPersistenceServices  = mock[PersistenceServices]\n    val mockConnectivityServices = mock[ConnectivityServices]\n    val mockAwarenessService     = mock[AwarenessServices]\n\n    val mockIntent         = mock[Intent]\n    val mockNineCardIntent = mock[NineCardsIntent]\n\n    val momentProcess = new MomentProcessImpl(\n      persistenceServices = mockPersistenceServices,\n      connectivityServices = mockConnectivityServices,\n      awarenessServices = mockAwarenessService)\n\n  }\n\n  trait BestAvailableMomentScope { self: MomentProcessScope =>\n\n    val time: DateTime\n\n    override val momentProcess = new MomentProcessImpl(\n      persistenceServices = mockPersistenceServices,\n      connectivityServices = mockConnectivityServices,\n      awarenessServices = mockAwarenessService) {\n\n      override protected def getNowDateTime = time\n    }\n  }\n\n}\n\nclass MomentProcessImplSpec extends MomentProcessImplSpecification {\n\n  \"getMoments\" should {\n\n    \"return the existing moments\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.fetchMoments returns TaskService(Task(Either.right(seqMoment)))\n        val result = momentProcess.getMoments.run\n        result shouldEqual Right(seqMoment)\n      }\n\n    \"returns MomentException when persistence services fails\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.fetchMoments returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        momentProcess.getMoments.mustLeft[MomentException]\n      }\n  }\n\n  \"getMomentByCollectionId\" should {\n\n    \"returns a collection for a valid request\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.getMomentByCollectionId(collectionId) returns serviceRight(\n          Some(moment.copy(collectionId = Option(collectionId))))\n\n        momentProcess.getMomentByCollectionId(collectionId).mustRight { maybeMoment =>\n          maybeMoment must beSome.which { resultMoment =>\n            resultMoment.id shouldEqual moment.id\n            resultMoment.collectionId shouldEqual Option(collectionId)\n          }\n        }\n      }\n\n    \"returns None for a valid request if the collection id doesn't exists\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.getMomentByCollectionId(collectionId) returns serviceRight(None)\n        momentProcess.getMomentByCollectionId(collectionId).mustRightNone\n      }\n\n    \"returns a CollectionException if the service throws an exception\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.getMomentByCollectionId(collectionId) returns serviceLeft(\n          persistenceServiceException)\n        momentProcess.getMomentByCollectionId(collectionId).mustLeft[MomentException]\n      }\n  }\n\n  \"getMomentByType\" should {\n\n    \"return a moment by type\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.getMomentByType(any) returns TaskService(\n          Task(Either.right(moment)))\n        val result = momentProcess.getMomentByType(momentType).run\n        result shouldEqual Right(moment)\n      }\n\n    \"returns MomentException when persistence services fails\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.getMomentByType(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        momentProcess.getMomentByType(momentType).mustLeft[MomentException]\n      }\n  }\n\n  \"fetchMomentByType\" should {\n\n    \"return a moment by type\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.fetchMomentByType(any) returns TaskService(\n          Task(Either.right(Option(moment))))\n        val result = momentProcess.fetchMomentByType(momentType).run\n        result shouldEqual Right(Option(moment))\n      }\n\n    \"returns MomentException when persistence services fails\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.fetchMomentByType(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        momentProcess.fetchMomentByType(momentType).mustLeft[MomentException]\n      }\n  }\n\n  \"findMoment\" should {\n\n    \"return a moment by id\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.fetchMomentById(any) returns TaskService(\n          Task(Either.right(Option(moment))))\n        val result = momentProcess.findMoment(moment.id).run\n        result shouldEqual Right(Option(moment))\n      }\n\n    \"returns MomentException when persistence services fails\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.fetchMomentById(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        momentProcess.findMoment(moment.id).mustLeft[MomentException]\n      }\n  }\n\n  \"updateMoments\" should {\n\n    \"return Unit for a valid request\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.updateMoment(any) returns TaskService(\n          Task(Either.right(updatedMoment)))\n        val result = momentProcess.updateMoment(moment)(contextSupport).run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"returns MomentException when persistence services fails\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.updateMoment(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        momentProcess.updateMoment(moment)(contextSupport).mustLeft[MomentException]\n      }\n\n  }\n\n  \"saveMoments\" should {\n\n    \"return the three moments saved\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.addMoments(any) returns TaskService(Task(Either.right(seqMoment)))\n        momentProcess.saveMoments(seqMomentData)(contextSupport).mustRight {\n          _.size shouldEqual seqMoment.size\n        }\n      }\n\n    \"returns MomentException when persistence services fails\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.addMoments(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        momentProcess.saveMoments(seqMomentData)(contextSupport).mustLeft[MomentException]\n      }\n\n  }\n\n  \"deleteAllMoments\" should {\n\n    \"returns a successful answer for a valid request\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.deleteAllMoments() returns TaskService(\n          Task(Either.right(momentId)))\n        val result = momentProcess.deleteAllMoments().run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"returns a MomentException if the service throws a exception deleting the moments\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.deleteAllMoments() returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        momentProcess.deleteAllMoments().mustLeft[MomentException]\n      }\n  }\n\n  \"deleteMoment\" should {\n\n    \"returns a successful answer for a valid request\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.deleteMoment(moment.id) returns TaskService.right(momentId)\n        val result = momentProcess.deleteMoment(moment.id).run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"returns a MomentException if the service throws a exception deleting the moment\" in\n      new MomentProcessScope {\n\n        mockPersistenceServices.deleteMoment(moment.id) returns TaskService.left(\n          persistenceServiceException)\n        val result = momentProcess.deleteMoment(moment.id).run\n        result must beAnInstanceOf[Left[MomentException, _]]\n      }\n  }\n\n  \"getBestAvailableMoment\" should {\n\n    \"returns the best available moment in the morning with the home's wifi available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(homeWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment(None, None)(contextSupport).run\n        result shouldEqual Right(Some(homeMoment))\n      }\n\n    \"returns the best available moment in the afternoon with the home's wifi available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowAfternoon\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(homeWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(homeMoment))\n      }\n\n    \"returns the best available moment in the night with the home's wifi available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowNight\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(homeWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(homeMoment))\n      }\n\n    \"returns the best available moment in the late night with the home's wifi available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowLateNight\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(homeWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(homeMoment))\n      }\n\n    \"returns the best available moment in the morning when two moments share the same wifi\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        val newWorkMoment = workMoment.copy(wifi = Seq(homeWifiSSID))\n\n        val moments = allMoments.map {\n          case Moment(_, _, _, _, _, _, WorkMoment, _) => newWorkMoment\n          case m                                       => m\n        }\n        mockPersistenceServices.fetchMoments returns TaskService.right(moments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(homeWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(newWorkMoment))\n      }\n\n    \"returns the best available moment in the morning when two moments share the same wifi but different hour range\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        val newWorkMoment = workMoment.copy(\n          wifi = Seq(homeWifiSSID),\n          timeslot = Seq(MomentTimeSlot(\"06:00\", \"20:00\", Seq.fill(7)(1))))\n\n        val moments = allMoments.map {\n          case Moment(_, _, _, _, _, _, WorkMoment, _) => newWorkMoment\n          case m                                       => m\n        }\n        mockPersistenceServices.fetchMoments returns TaskService.right(moments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(homeWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(homeMoment))\n      }\n\n    \"returns the best available moment in the morning when two moments share the same wifi but only one is in range\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        val newWorkMoment = workMoment.copy(\n          wifi = Seq(homeWifiSSID),\n          timeslot = Seq(MomentTimeSlot(\"16:00\", \"20:00\", Seq.fill(7)(1))))\n\n        val moments = allMoments.map {\n          case Moment(_, _, _, _, _, _, WorkMoment, _) => newWorkMoment\n          case m                                       => m\n        }\n        mockPersistenceServices.fetchMoments returns TaskService.right(moments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(homeWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(homeMoment))\n      }\n\n    \"returns the best available moment in the morning with no wifi available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(None)\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(studyMoment))\n      }\n\n    \"returns the best available moment in the afternoon with no wifi available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowAfternoon\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(None)\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(outAndAboutMoment))\n      }\n\n    \"returns the best available moment in the night with no wifi available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowNight\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(None)\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(nightMoment))\n      }\n\n    \"returns the best available moment when the headphones are sent\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(None)\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result =\n          momentProcess.getBestAvailableMoment(maybeHeadphones = Some(true))(contextSupport).run\n        result shouldEqual Right(Some(musicMoment))\n      }\n\n    \"returns the best available moment when the headphones state is active\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(true))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(None)\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(musicMoment))\n      }\n\n    \"returns the best available moment when the headphones are sent but a wifi is available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(workWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result =\n          momentProcess.getBestAvailableMoment(maybeHeadphones = Some(true))(contextSupport).run\n        result shouldEqual Right(Some(musicMoment))\n      }\n\n    \"returns the best available moment when the headphones state is active but a wifi is available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(true))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(workWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(musicMoment))\n      }\n\n    \"returns the best available moment when the headphones state is active but a wifi is available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(workWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(workMoment))\n      }\n\n    \"returns the best available moment when the headphones state is active but there is no music moment available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(\n          allMoments.filterNot(_.momentType == MusicMoment))\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(None)\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(studyMoment))\n      }\n\n    \"returns the best available moment when the InVehicleActivity is sent\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(None)\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess\n          .getBestAvailableMoment(maybeActivity = Some(InVehicleActivity))(contextSupport)\n          .run\n        result shouldEqual Right(Some(carMoment))\n      }\n\n    \"returns the best available moment when the InVehicleActivity is active\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(InVehicleActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(None)\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(carMoment))\n      }\n\n    \"returns the best available moment when the InVehicleActivity is sent but a wifi is available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(workWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess\n          .getBestAvailableMoment(maybeActivity = Some(InVehicleActivity))(contextSupport)\n          .run\n        result shouldEqual Right(Some(workMoment))\n      }\n\n    \"returns the best available moment when the InVehicleActivity is active but a wifi is available\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(allMoments)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(InVehicleActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(workWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(workMoment))\n      }\n\n    \"returns the best available moment when the only moment available is the default one\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(Seq(outAndAboutMoment))\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(workWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(outAndAboutMoment))\n      }\n\n    \"returns the best available moment and add it to the DB when the default one doesn't exists\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowAfternoon\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(\n          allMoments.filterNot(_.momentType.isDefault))\n        mockPersistenceServices.addMoment(any) returns TaskService.right(outAndAboutMoment)\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(None)\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(outAndAboutMoment))\n\n        there was one(mockPersistenceServices).addMoment(\n          outAndAboutMoment.toData.copy(collectionId = None))\n      }\n\n    \"returns the best available moment when only one moment is happening\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        val moment1 = homeMoment\n          .copy(wifi = Seq.empty, timeslot = Seq(MomentTimeSlot(\"06:00\", \"18:00\", Seq.fill(7)(1))))\n        val moment2 = workMoment\n          .copy(wifi = Seq.empty, timeslot = Seq(MomentTimeSlot(\"18:00\", \"23:00\", Seq.fill(7)(1))))\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(Seq(moment1, moment2))\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(None)\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(moment1))\n      }\n\n    \"returns the best available moment when both moments are happening but one has a lowest range\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        val moment1 = homeMoment\n          .copy(wifi = Seq.empty, timeslot = Seq(MomentTimeSlot(\"06:00\", \"18:00\", Seq.fill(7)(1))))\n        val moment2 = workMoment\n          .copy(wifi = Seq.empty, timeslot = Seq(MomentTimeSlot(\"06:00\", \"12:00\", Seq.fill(7)(1))))\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(Seq(moment1, moment2))\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(None)\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(Set.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(moment2))\n      }\n\n    \"returns the best available moment with Bluetooth if the same bluetooth is connected\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        val bluetoothName = \"Bluetooth\"\n\n        val moment1 = homeMoment.copy(\n          wifi = Seq.empty,\n          bluetooth = Seq(bluetoothName),\n          timeslot = Seq(MomentTimeSlot(\"06:00\", \"18:00\", Seq.fill(7)(1))))\n        val moment2 = workMoment\n          .copy(wifi = Seq.empty, timeslot = Seq(MomentTimeSlot(\"06:00\", \"12:00\", Seq.fill(7)(1))))\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(Seq(moment1, moment2))\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(None)\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(\n          Set(bluetoothName))\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(moment1))\n      }\n\n    \"returns the best available moment with Bluetooth if the same bluetooth is connected and wifi is connected too\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        val bluetoothName = \"Bluetooth\"\n\n        val moment1 = homeMoment.copy(\n          wifi = Seq.empty,\n          bluetooth = Seq(bluetoothName),\n          timeslot = Seq(MomentTimeSlot(\"06:00\", \"18:00\", Seq.fill(7)(1))))\n        val moment2 = workMoment\n          .copy(wifi = Seq.empty, timeslot = Seq(MomentTimeSlot(\"06:00\", \"12:00\", Seq.fill(7)(1))))\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(Seq(moment1, moment2))\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(homeWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(\n          Set(bluetoothName))\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(moment1))\n      }\n\n    \"returns the best available moment when both moments are happening and have the same Bluetooth device but one has a lowest range\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        val bluetoothName = \"Bluetooth\"\n\n        val moment1 = homeMoment.copy(\n          wifi = Seq.empty,\n          bluetooth = Seq(bluetoothName),\n          timeslot = Seq(MomentTimeSlot(\"06:00\", \"18:00\", Seq.fill(7)(1))))\n        val moment2 = workMoment.copy(\n          wifi = Seq.empty,\n          bluetooth = Seq(bluetoothName),\n          timeslot = Seq(MomentTimeSlot(\"06:00\", \"12:00\", Seq.fill(7)(1))))\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(Seq(moment1, moment2))\n        mockAwarenessService.getHeadphonesState returns TaskService.right(Headphones(false))\n        mockAwarenessService.getTypeActivity returns TaskService.right(\n          ProbablyActivity(UnknownActivity))\n        mockConnectivityServices.getCurrentSSID(any) returns TaskService.right(Some(homeWifiSSID))\n        mockConnectivityServices.getBluetoothConnected(any) returns TaskService.right(\n          Set(bluetoothName))\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(Some(moment2))\n      }\n\n    \"returns None when there is no moments in the database\" in\n      new MomentProcessScope with BestAvailableMomentScope {\n\n        override val time = nowMorning\n\n        mockPersistenceServices.fetchMoments returns TaskService.right(Seq.empty)\n\n        val result = momentProcess.getBestAvailableMoment()(contextSupport).run\n        result shouldEqual Right(None)\n      }\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/recognition/impl/RecognitionProcessData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.recognition.impl\n\nimport cards.nine.models._\nimport cards.nine.models.types.{ClearCondition, CloudyCondition, FoggyCondition, InVehicleActivity}\n\nimport scala.util.Random\n\ntrait RecognitionProcessData {\n\n  val latitude     = Random.nextDouble()\n  val longitude    = Random.nextDouble()\n  val countryCode  = Some(\"ES\")\n  val countryName  = Some(\"Spain\")\n  val addressLines = Seq(\"street\", \"city\", \"postal code\")\n\n  val humidity              = Random.nextInt(100)\n  val dewPointCelsius       = Random.nextFloat()\n  val dewPointFahrenheit    = Random.nextFloat()\n  val temperatureCelsius    = Random.nextFloat()\n  val temperatureFahrenheit = Random.nextFloat()\n\n  val kindActivityService = InVehicleActivity\n\n  val kindActivityProcess = InVehicleActivity\n\n  val typeActivity = ProbablyActivity(activityType = kindActivityService)\n\n  val probablyActivity = ProbablyActivity(activityType = kindActivityProcess)\n\n  val connected = Random.nextBoolean()\n\n  val headphonesState = Headphones(connected = connected)\n\n  val headphones = Headphones(connected = connected)\n\n  val awarenessLocation = Location(\n    latitude = latitude,\n    longitude = longitude,\n    countryCode = countryCode,\n    countryName = countryName,\n    addressLines = addressLines)\n\n  val location = Location(\n    latitude = latitude,\n    longitude = longitude,\n    countryCode = countryCode,\n    countryName = countryName,\n    addressLines = addressLines)\n\n  val conditionsServices = Seq(ClearCondition, CloudyCondition, FoggyCondition)\n\n  val conditionsProcess = Seq(ClearCondition, CloudyCondition, FoggyCondition)\n\n  val weatherState = WeatherState(\n    conditions = conditionsServices,\n    humidity = humidity,\n    dewPointCelsius = dewPointCelsius,\n    dewPointFahrenheit = dewPointFahrenheit,\n    temperatureCelsius = temperatureCelsius,\n    temperatureFahrenheit = temperatureFahrenheit)\n\n  val weather = WeatherState(\n    conditions = conditionsProcess,\n    humidity = humidity,\n    dewPointCelsius = dewPointCelsius,\n    dewPointFahrenheit = dewPointFahrenheit,\n    temperatureCelsius = temperatureCelsius,\n    temperatureFahrenheit = temperatureFahrenheit)\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/recognition/impl/RecognitionProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.recognition.impl\n\nimport android.content.BroadcastReceiver\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.MomentTestData\nimport cards.nine.models.types._\nimport cards.nine.process.recognition.RecognitionProcessException\nimport cards.nine.services.awareness.{AwarenessException, AwarenessServices}\nimport cards.nine.services.persistence.PersistenceServices\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait RecognitionProcessSpecification\n    extends TaskServiceSpecification\n    with Mockito\n    with RecognitionProcessData\n    with MomentTestData {\n\n  val awarenessException = AwarenessException(\"Irrelevant message\")\n\n  trait RecognitionProcessScope extends Scope {\n\n    val contextSupport = mock[ContextSupport]\n\n    val mockPersistenceServices = mock[PersistenceServices]\n    val mockServices            = mock[AwarenessServices]\n\n    val receiver = mock[BroadcastReceiver]\n\n    val process = new RecognitionProcessImpl(mockPersistenceServices, mockServices)\n\n  }\n}\n\nclass RecognitionProcessImplSpec extends RecognitionProcessSpecification {\n\n  \"getMostProbableActivity\" should {\n\n    \"get the most probable activity\" in new RecognitionProcessScope {\n\n      mockServices.getTypeActivity returns TaskService(Task(Either.right(typeActivity)))\n\n      val result = process.getMostProbableActivity.run\n      result shouldEqual Either.right(probablyActivity)\n\n      there was one(mockServices).getTypeActivity\n    }\n\n    \"return a RecognitionProcessException when the service return an exception\" in new RecognitionProcessScope {\n\n      mockServices.getTypeActivity returns TaskService(Task(Either.left(awarenessException)))\n\n      process.getMostProbableActivity.mustLeft[RecognitionProcessException]\n    }\n\n  }\n\n  \"registerFenceUpdates\" should {\n\n    \"call to register updates with the right fences\" in new RecognitionProcessScope {\n\n      val moment1 = moment.copy(momentType = MusicMoment)\n      val moment4 = moment.copy(momentType = CarMoment)\n\n      mockPersistenceServices.fetchMoments returns TaskService.right(Seq(moment1, moment4))\n      mockServices.registerFenceUpdates(any, any, any)(any) returns TaskService.empty\n\n      val result = process.registerFenceUpdates(\"\", receiver)(contextSupport).run\n      result shouldEqual Either.right((): Unit)\n\n      there was one(mockServices)\n        .registerFenceUpdates(\"\", Seq(HeadphonesFence, InVehicleFence), receiver)(contextSupport)\n    }\n\n    \"return unit when the're no moments in the database\" in new RecognitionProcessScope {\n\n      mockPersistenceServices.fetchMoments returns TaskService.right(Seq.empty)\n      mockServices.registerFenceUpdates(any, any, any)(any) returns TaskService(\n        Task(Either.left(awarenessException)))\n\n      process.registerFenceUpdates(\"\", receiver)(contextSupport).run shouldEqual Either.right(\n        (): Unit)\n    }\n\n    \"return a RecognitionProcessException when the service return an exception\" in new RecognitionProcessScope {\n\n      val moment1 = moment.copy(momentType = MusicMoment)\n      mockPersistenceServices.fetchMoments returns TaskService.right(Seq(moment1))\n      mockServices.registerFenceUpdates(any, any, any)(any) returns TaskService(\n        Task(Either.left(awarenessException)))\n\n      process\n        .registerFenceUpdates(\"\", receiver)(contextSupport)\n        .mustLeft[RecognitionProcessException]\n    }\n\n  }\n\n  \"getHeadphone\" should {\n\n    \"get headphone status\" in new RecognitionProcessScope {\n\n      mockServices.getHeadphonesState returns TaskService(Task(Either.right(headphonesState)))\n\n      val result = process.getHeadphone.run\n      result shouldEqual Either.right(headphones)\n\n      there was one(mockServices).getHeadphonesState\n    }\n\n    \"return a RecognitionProcessException when the service return an exception\" in new RecognitionProcessScope {\n\n      mockServices.getHeadphonesState returns TaskService(Task(Either.left(awarenessException)))\n\n      process.getHeadphone.mustLeft[RecognitionProcessException]\n    }\n\n  }\n\n  \"getLocation\" should {\n\n    \"get location\" in new RecognitionProcessScope {\n\n      mockServices.getLocation(any) returns TaskService(Task(Either.right(awarenessLocation)))\n\n      val result = process.getLocation(contextSupport).run\n      result shouldEqual Either.right(location)\n\n      there was one(mockServices).getLocation(any)\n    }\n\n    \"return a RecognitionProcessException when the service return an exception\" in new RecognitionProcessScope {\n\n      mockServices.getLocation(any) returns TaskService(Task(Either.left(awarenessException)))\n\n      process.getLocation(contextSupport).mustLeft[RecognitionProcessException]\n    }\n\n  }\n\n  \"getWeather\" should {\n\n    \"get weather\" in new RecognitionProcessScope {\n\n      mockServices.getWeather returns TaskService(Task(Either.right(weatherState)))\n\n      val result = process.getWeather.run\n      result shouldEqual Either.right(weather)\n\n      there was one(mockServices).getWeather\n    }\n\n    \"return a RecognitionProcessException when the service return an exception\" in new RecognitionProcessScope {\n\n      mockServices.getWeather returns TaskService(Task(Either.left(awarenessException)))\n\n      process.getWeather.mustLeft[RecognitionProcessException]\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/recommendations/impl/RecommendationsProcessSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.recommendations.impl\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.ApiTestData\nimport cards.nine.commons.test.data.ApiValues._\nimport cards.nine.commons.test.data.UserV1Values._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.process.recommendations.{\n  RecommendedAppsConfigurationException,\n  RecommendedAppsException\n}\nimport cards.nine.process.utils.ApiUtils\nimport cards.nine.services.api.{ApiServiceConfigurationException, ApiServiceException, ApiServices}\nimport cards.nine.services.persistence.PersistenceServices\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\nimport scala.reflect.ClassTag\n\ntrait RecommendationsProcessSpecification extends Specification with Mockito with ApiTestData {\n\n  val apiException       = ApiServiceException(\"\")\n  val apiConfigException = ApiServiceConfigurationException(\"\")\n\n  trait RecommendationsProcessScope extends Scope {\n\n    val contextSupport = mock[ContextSupport]\n\n    val apiServices = mock[ApiServices]\n\n    val mockPersistenceServices = mock[PersistenceServices]\n\n    val process = new RecommendationsProcessImpl(apiServices, mockPersistenceServices) {\n\n      override val apiUtils: ApiUtils = mock[ApiUtils]\n      apiUtils.getRequestConfig(contextSupport) returns\n        TaskService(Task(Either.right(requestConfig)))\n    }\n\n    def mustLeft[T <: NineCardException](service: TaskService[_])(\n        implicit classTag: ClassTag[T]): Unit =\n      service.value.run must beLike {\n        case Left(e) => e must beAnInstanceOf[T]\n      }\n\n  }\n\n}\n\nclass RecommendationsProcessSpec extends RecommendationsProcessSpecification {\n\n  \"getRecommendedApps\" should {\n\n    \"return an equivalent sequence to the returned by the Service\" in\n      new RecommendationsProcessScope {\n\n        apiServices.getRecommendedApps(any, any, any)(any) returns\n          TaskService(Task(Either.right(seqNotCategorizedPackage)))\n\n        val result = process.getRecommendedAppsByCategory(category)(contextSupport).value.run\n\n        there was one(apiServices).getRecommendedApps(category.name, Seq.empty, limit)(\n          requestConfig)\n\n        result must beLike {\n          case Right(response) =>\n            response.seq.map(_.packageName).toSet shouldEqual seqNotCategorizedPackage\n              .map(_.packageName)\n              .toSet\n        }\n\n      }\n\n    \"returns a RecommendedAppsException if service returns an exception\" in\n      new RecommendationsProcessScope {\n\n        apiServices.getRecommendedApps(any, any, any)(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[RecommendedAppsException](\n          process.getRecommendedAppsByCategory(category)(contextSupport))\n      }\n\n    \"returns a RecommendedAppsConfigurationException if service returns a config exception\" in\n      new RecommendationsProcessScope {\n\n        apiServices.getRecommendedApps(any, any, any)(any) returns\n          TaskService(Task(Either.left(apiConfigException)))\n\n        mustLeft[RecommendedAppsConfigurationException](\n          process.getRecommendedAppsByCategory(category)(contextSupport))\n      }\n\n  }\n\n  \"getRecommendedAppsByPackages\" should {\n\n    \"return an equivalent sequence to the returned by the Service\" in\n      new RecommendationsProcessScope {\n\n        apiServices.getRecommendedAppsByPackages(any, any, any)(any) returns\n          TaskService(Task(Either.right(seqNotCategorizedPackage)))\n\n        val result = process.getRecommendedAppsByPackages(likePackages)(contextSupport).value.run\n\n        there was one(apiServices).getRecommendedAppsByPackages(likePackages, Seq.empty, limit)(\n          requestConfig)\n\n        result must beLike {\n          case Right(response) =>\n            response.seq.map(_.packageName).toSet shouldEqual seqNotCategorizedPackage\n              .map(_.packageName)\n              .toSet\n        }\n\n      }\n\n    \"returns a RecommendedAppsException if service returns an exception\" in\n      new RecommendationsProcessScope {\n\n        apiServices.getRecommendedAppsByPackages(any, any, any)(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[RecommendedAppsException](\n          process.getRecommendedAppsByPackages(likePackages)(contextSupport))\n      }\n\n    \"returns a RecommendedAppsConfigurationException if service returns an exception\" in\n      new RecommendationsProcessScope {\n\n        apiServices.getRecommendedAppsByPackages(any, any, any)(any) returns\n          TaskService(Task(Either.left(apiConfigException)))\n\n        mustLeft[RecommendedAppsConfigurationException](\n          process.getRecommendedAppsByPackages(likePackages)(contextSupport))\n      }\n\n  }\n\n  \"searchApps\" should {\n\n    \"return an equivalent sequence to the returned by the Service\" in\n      new RecommendationsProcessScope {\n\n        apiServices.searchApps(any, any, any)(any) returns\n          TaskService(Task(Either.right(seqNotCategorizedPackage)))\n\n        val result = process.searchApps(searchString, excludedPackages)(contextSupport).value.run\n\n        there was one(apiServices).searchApps(searchString, excludedPackages, limit)(requestConfig)\n\n        result must beLike {\n          case Right(response) =>\n            response.seq.map(_.packageName).toSet shouldEqual seqNotCategorizedPackage\n              .map(_.packageName)\n              .toSet\n        }\n\n      }\n\n    \"returns a RecommendedAppsException if service returns an exception\" in\n      new RecommendationsProcessScope {\n\n        apiServices.searchApps(any, any, any)(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[RecommendedAppsException](\n          process.searchApps(searchString, excludedPackages)(contextSupport))\n      }\n\n    \"returns a RecommendedAppsConfigurationException if service returns an exception\" in\n      new RecommendationsProcessScope {\n\n        apiServices.searchApps(any, any, any)(any) returns\n          TaskService(Task(Either.left(apiConfigException)))\n\n        mustLeft[RecommendedAppsConfigurationException](\n          process.searchApps(searchString, excludedPackages)(contextSupport))\n      }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/sharedcollections/impl/SharedCollectionsProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.sharedcollections.impl\n\nimport android.content.pm.PackageManager\nimport android.content.res.Resources\nimport android.util.DisplayMetrics\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.ApiValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.SharedCollectionValues._\nimport cards.nine.commons.test.data.{ApiTestData, CollectionTestData, SharedCollectionTestData}\nimport cards.nine.models.types._\nimport cards.nine.process.sharedcollections.{\n  SharedCollectionsConfigurationException,\n  SharedCollectionsException\n}\nimport cards.nine.process.utils.ApiUtils\nimport cards.nine.services.api.{ApiServiceConfigurationException, ApiServiceException, ApiServices}\nimport cards.nine.services.persistence.PersistenceServices\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\nimport scala.reflect.ClassTag\n\ntrait SharedCollectionsProcessImplSpecification extends Specification with Mockito {\n\n  val apiException       = ApiServiceException(\"\")\n  val apiConfigException = ApiServiceConfigurationException(\"\")\n\n  trait SharedCollectionsProcessProcessScope\n      extends Scope\n      with ApiTestData\n      with CollectionTestData\n      with SharedCollectionTestData {\n\n    val resources = mock[Resources]\n    resources.getDisplayMetrics returns mock[DisplayMetrics]\n\n    val contextSupport = mock[ContextSupport]\n    contextSupport.getPackageManager returns mock[PackageManager]\n    contextSupport.getResources returns resources\n\n    val mockApiServices = mock[ApiServices]\n\n    val mockPersistenceServices = mock[PersistenceServices]\n\n    val sharedCollectionsProcess = new SharedCollectionsProcessImpl(\n      apiServices = mockApiServices,\n      persistenceServices = mockPersistenceServices) {\n\n      override val apiUtils: ApiUtils = mock[ApiUtils]\n      apiUtils.getRequestConfig(contextSupport) returns\n        TaskService(Task(Either.right(requestConfig)))\n    }\n\n    def mustLeft[T <: NineCardException](service: TaskService[_])(\n        implicit classTag: ClassTag[T]): Unit =\n      service.value.run must beLike {\n        case Left(e) => e must beAnInstanceOf[T]\n      }\n\n  }\n\n}\n\nclass SharedCollectionsProcessImplSpec extends SharedCollectionsProcessImplSpecification {\n\n  \"getSharedCollection\" should {\n\n    \"returns a collection for a valid request\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getSharedCollection(anyString)(any) returns\n          TaskService(Task(Either.right(sharedCollection)))\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(any) returns\n          TaskService(Task(Either.right(None)))\n\n        val result = sharedCollectionsProcess\n          .getSharedCollection(sharedCollectionId)(contextSupport)\n          .value\n          .run\n        result must beLike {\n          case Right(shareCollection) =>\n            shareCollection shouldEqual sharedCollection.copy(locallyAdded = Some(false))\n            shareCollection.publicCollectionStatus shouldEqual PublishedByOther\n        }\n      }\n\n    \"returns a collection marked as PublishedByMe\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getSharedCollection(anyString)(any) returns\n          TaskService(Task(Either.right(sharedCollection)))\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(any) returns\n          TaskService(\n            Task(Either.right(Option(collection.copy(publicCollectionStatus = PublishedByMe)))))\n\n        val result = sharedCollectionsProcess\n          .getSharedCollection(sharedCollectionId)(contextSupport)\n          .value\n          .run\n        result must beLike {\n          case Right(shareCollection) =>\n            shareCollection shouldEqual sharedCollection\n              .copy(publicCollectionStatus = PublishedByMe, locallyAdded = Some(true))\n        }\n      }\n\n    \"returns a sequence of shared collections for a valid request where the first one is marked as PublishedByOther\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getSharedCollection(anyString)(any) returns\n          TaskService(Task(Either.right(sharedCollection)))\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(any) returns\n          TaskService(\n            Task(Either.right(Option(collection.copy(publicCollectionStatus = PublishedByOther)))))\n\n        val result = sharedCollectionsProcess\n          .getSharedCollection(sharedCollectionId)(contextSupport)\n          .value\n          .run\n        result must beLike {\n          case Right(shareCollection) =>\n            shareCollection shouldEqual sharedCollection\n              .copy(publicCollectionStatus = PublishedByOther, locallyAdded = Some(true))\n        }\n      }\n\n    \"returns a sequence of shared collections for a valid request where the first one is marked as NotPublished\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getSharedCollection(anyString)(any) returns\n          TaskService(Task(Either.right(sharedCollection)))\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(any) returns\n          TaskService(Task(Either.right(Option(collection))))\n\n        val result = sharedCollectionsProcess\n          .getSharedCollection(sharedCollectionId)(contextSupport)\n          .value\n          .run\n        result must beLike {\n          case Right(shareCollection) =>\n            shareCollection shouldEqual sharedCollection.copy(locallyAdded = Some(true))\n        }\n      }\n\n    \"returns a SharedCollectionsException if the service throws an exception\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getSharedCollection(anyString)(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.getSharedCollection(sharedCollectionId)(contextSupport))\n      }\n\n    \"returns a SharedCollectionsConfigurationException if the service throws a config exception\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getSharedCollection(anyString)(any) returns\n          TaskService(Task(Either.left(apiConfigException)))\n\n        mustLeft[SharedCollectionsConfigurationException](\n          sharedCollectionsProcess.getSharedCollection(sharedCollectionId)(contextSupport))\n      }\n  }\n\n  \"getSharedCollectionsByCategory\" should {\n\n    \"returns a sequence of shared collections for a valid request\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getSharedCollectionsByCategory(anyString, anyString, anyInt, anyInt)(any) returns\n          TaskService(Task(Either.right(seqSharedCollection)))\n        mockPersistenceServices.fetchCollectionsBySharedCollectionIds(any) returns\n          TaskService(Task(Either.right(Seq.empty)))\n\n        val result = sharedCollectionsProcess\n          .getSharedCollectionsByCategory(category, typeShareCollection, offset, limit)(\n            contextSupport)\n          .value\n          .run\n        result must beLike {\n          case Right(shareCollections) =>\n            shareCollections.size shouldEqual seqSharedCollection.size\n            shareCollections map (_.name) shouldEqual seqSharedCollection.map(_.name)\n            forall(shareCollections map (_.publicCollectionStatus))(\n              (_: PublicCollectionStatus) shouldEqual PublishedByOther)\n        }\n      }\n\n    \"returns a sequence of shared collections for a valid request where the first one is marked as PublishedByMe\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getSharedCollectionsByCategory(anyString, anyString, anyInt, anyInt)(any) returns\n          TaskService(Task(Either.right(seqSharedCollection)))\n        val seqFetchedCollection = seqCollection map (_.copy(\n            publicCollectionStatus = PublishedByMe))\n        mockPersistenceServices.fetchCollectionsBySharedCollectionIds(any) returns\n          TaskService(Task(Either.right(seqFetchedCollection)))\n\n        val result = sharedCollectionsProcess\n          .getSharedCollectionsByCategory(category, typeShareCollection, offset, limit)(\n            contextSupport)\n          .value\n          .run\n        result must beLike {\n          case Right(shareCollections) =>\n            shareCollections.size shouldEqual seqSharedCollection.size\n            shareCollections map (_.name) shouldEqual seqSharedCollection.map(_.name)\n            forall(shareCollections map (_.publicCollectionStatus))(\n              (_: PublicCollectionStatus) shouldEqual PublishedByMe)\n        }\n      }\n\n    \"returns a sequence of shared collections for a valid request where the first one is marked as PublishedByOther\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getSharedCollectionsByCategory(anyString, anyString, anyInt, anyInt)(any) returns\n          TaskService(Task(Either.right(seqSharedCollection)))\n        val seqFetchedCollection = seqCollection map (_.copy(\n            publicCollectionStatus = PublishedByOther))\n        mockPersistenceServices.fetchCollectionsBySharedCollectionIds(any) returns\n          TaskService(Task(Either.right(seqFetchedCollection)))\n\n        val result = sharedCollectionsProcess\n          .getSharedCollectionsByCategory(category, typeShareCollection, offset, limit)(\n            contextSupport)\n          .value\n          .run\n        result must beLike {\n          case Right(shareCollections) =>\n            shareCollections.size shouldEqual seqSharedCollection.size\n            shareCollections map (_.name) shouldEqual seqSharedCollection.map(_.name)\n            forall(shareCollections map (_.publicCollectionStatus))(\n              (_: PublicCollectionStatus) shouldEqual PublishedByOther)\n        }\n      }\n\n    \"returns a SharedCollectionsException if the service throws an exception\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getSharedCollectionsByCategory(anyString, anyString, anyInt, anyInt)(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.getSharedCollectionsByCategory(\n            category = category,\n            typeShareCollection = typeShareCollection,\n            offset = offset,\n            limit = limit)(contextSupport))\n      }\n\n    \"returns a SharedCollectionsConfigurationException if the service throws a config exception\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getSharedCollectionsByCategory(anyString, anyString, anyInt, anyInt)(any) returns\n          TaskService(Task(Either.left(apiConfigException)))\n\n        mustLeft[SharedCollectionsConfigurationException](\n          sharedCollectionsProcess.getSharedCollectionsByCategory(\n            category = category,\n            typeShareCollection = typeShareCollection,\n            offset = offset,\n            limit = limit)(contextSupport))\n      }\n  }\n\n  \"getPublishedCollections\" should {\n\n    \"returns a sequence of published collections for a valid request\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getPublishedCollections()(any) returns\n          TaskService(Task(Either.right(seqSharedCollection)))\n        mockPersistenceServices.fetchCollectionsBySharedCollectionIds(any) returns\n          TaskService(Task(Either.right(Seq.empty)))\n\n        val result = sharedCollectionsProcess.getPublishedCollections()(contextSupport).value.run\n        result must beLike {\n          case Right(shareCollections) =>\n            shareCollections.size shouldEqual seqSharedCollection.size\n            shareCollections map (_.name) shouldEqual seqSharedCollection.map(_.name)\n            forall(shareCollections map (_.publicCollectionStatus))(\n              (_: PublicCollectionStatus) shouldEqual PublishedByOther)\n        }\n      }\n\n    \"returns a sequence of shared collections for a valid request where the first one is marked as PublishedByMe\" in\n      new SharedCollectionsProcessProcessScope {\n\n        val seqSharedCollectionPublishedByMe = seqSharedCollection map (_.copy(\n            publicCollectionStatus = PublishedByMe))\n        mockApiServices.getPublishedCollections()(any) returns\n          TaskService(Task(Either.right(seqSharedCollectionPublishedByMe)))\n        val seqFetchedCollection = seqCollection map (_.copy(\n            publicCollectionStatus = PublishedByMe))\n        mockPersistenceServices.fetchCollectionsBySharedCollectionIds(any) returns\n          TaskService(Task(Either.right(seqFetchedCollection)))\n\n        val result = sharedCollectionsProcess.getPublishedCollections()(contextSupport).value.run\n        result must beLike {\n          case Right(shareCollections) =>\n            shareCollections.size shouldEqual seqSharedCollectionPublishedByMe.size\n            shareCollections map (_.name) shouldEqual seqSharedCollectionPublishedByMe.map(_.name)\n            forall(shareCollections map (_.publicCollectionStatus))(\n              (_: PublicCollectionStatus) shouldEqual PublishedByMe)\n        }\n      }\n\n    \"returns a sequence of shared collections for a valid request where the first one is marked as PublishedByOther\" in\n      new SharedCollectionsProcessProcessScope {\n\n        val seqSharedCollectionPublishedByOther = seqSharedCollection map (_.copy(\n            publicCollectionStatus = PublishedByOther))\n        mockApiServices.getPublishedCollections()(any) returns\n          TaskService(Task(Either.right(seqSharedCollectionPublishedByOther)))\n        val seqFetchedCollection = seqCollection map (_.copy(\n            publicCollectionStatus = PublishedByOther))\n        mockPersistenceServices.fetchCollectionsBySharedCollectionIds(any) returns\n          TaskService(Task(Either.right(seqFetchedCollection)))\n\n        val result = sharedCollectionsProcess.getPublishedCollections()(contextSupport).value.run\n        result must beLike {\n          case Right(shareCollections) =>\n            shareCollections.size shouldEqual seqSharedCollectionPublishedByOther.size\n            shareCollections map (_.name) shouldEqual seqSharedCollectionPublishedByOther.map(\n              _.name)\n            forall(shareCollections map (_.publicCollectionStatus))(\n              (_: PublicCollectionStatus) shouldEqual PublishedByOther)\n        }\n      }\n\n    \"returns a SharedCollectionsException if the service throws an exception\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getPublishedCollections()(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.getPublishedCollections()(contextSupport))\n      }\n\n    \"returns a SharedCollectionsConfigurationException if the service throws a config exception\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.getPublishedCollections()(any) returns\n          TaskService(Task(Either.left(apiConfigException)))\n\n        mustLeft[SharedCollectionsConfigurationException](\n          sharedCollectionsProcess.getPublishedCollections()(contextSupport))\n      }\n  }\n\n  \"createSharedCollection\" should {\n\n    \"successfully create a collection for a valid request\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.createSharedCollection(\n          anyString,\n          anyString,\n          any,\n          anyString,\n          anyString,\n          any)(any) returns\n          TaskService(Task(Either.right(sharedCollectionId)))\n\n        val result = sharedCollectionsProcess\n          .createSharedCollection(\n            sharedCollectionName,\n            author,\n            sharedCollectionPackageNamesStr,\n            category,\n            sharedCollectionIcon,\n            community)(contextSupport)\n          .value\n          .run\n\n        result shouldEqual Right(sharedCollectionId)\n      }\n\n    \"return a SharedCollectionsException if the service throws an exception\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.createSharedCollection(\n          anyString,\n          anyString,\n          any,\n          anyString,\n          anyString,\n          any)(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.createSharedCollection(\n            sharedCollectionName,\n            author,\n            sharedCollectionPackageNamesStr,\n            category,\n            sharedCollectionIcon,\n            community)(contextSupport))\n      }\n\n    \"return a SharedCollectionsConfigurationException if the service throws a config exception\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.createSharedCollection(\n          anyString,\n          anyString,\n          any,\n          anyString,\n          anyString,\n          any)(any) returns\n          TaskService(Task(Either.left(apiConfigException)))\n\n        mustLeft[SharedCollectionsConfigurationException](\n          sharedCollectionsProcess.createSharedCollection(\n            sharedCollectionName,\n            author,\n            sharedCollectionPackageNamesStr,\n            category,\n            sharedCollectionIcon,\n            community)(contextSupport))\n      }\n  }\n\n  \"updateShareCollection\" should {\n\n    \"successfully create a collection for a valid request\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.updateSharedCollection(any, any, any)(any) returns TaskService(\n          Task(Either.right(sharedCollectionId)))\n\n        val result = sharedCollectionsProcess\n          .updateSharedCollection(\n            sharedCollectionId,\n            sharedCollectionName,\n            sharedCollectionPackageNamesStr)(contextSupport)\n          .value\n          .run\n\n        result shouldEqual Right(sharedCollectionId)\n      }\n\n    \"return a SharedCollectionsException if the service throws an exception\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.updateSharedCollection(any, any, any)(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.updateSharedCollection(\n            sharedCollectionId,\n            sharedCollectionName,\n            sharedCollectionPackageNamesStr)(contextSupport))\n      }\n\n    \"return a SharedCollectionsConfigurationException if the service throws a config exception\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.updateSharedCollection(any, any, any)(any) returns\n          TaskService(Task(Either.left(apiConfigException)))\n\n        mustLeft[SharedCollectionsConfigurationException](\n          sharedCollectionsProcess.updateSharedCollection(\n            sharedCollectionId,\n            sharedCollectionName,\n            sharedCollectionPackageNamesStr)(contextSupport))\n      }\n  }\n\n  \"getSubscriptions\" should {\n\n    \"returns a sequence of the subscriptions for a valid request\" in\n      new SharedCollectionsProcessProcessScope {\n        mockPersistenceServices.fetchCollections returns TaskService(\n          Task(Either.right(seqCollection)))\n\n        val result = sharedCollectionsProcess.getSubscriptions()(contextSupport).value.run\n\n        result must beLike {\n          case Right(seqSubscription) =>\n            seqSubscription.size shouldEqual seqPublicCollection.size\n            seqSubscription map (_.sharedCollectionId) shouldEqual seqPublicCollection.map(_._1)\n        }\n      }\n\n    \"returns a SharedCollectionsException if the service throws an exception getting the collections\" in\n      new SharedCollectionsProcessProcessScope {\n        mockPersistenceServices.fetchCollections returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.getSubscriptions()(contextSupport))\n      }\n  }\n\n  \"subscribe\" should {\n\n    \"returns a sequence of the subscriptions for a valid request\" in\n      new SharedCollectionsProcessProcessScope {\n        mockApiServices.subscribe(any)(any) returns\n          TaskService(Task(Either.right((): Unit)))\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(any) returns\n          TaskService(Task(Either.right(Option(collection))))\n        mockPersistenceServices.updateCollection(any) returns\n          TaskService(Task(Either.right(1)))\n\n        val result =\n          sharedCollectionsProcess.subscribe(sharedCollectionId)(contextSupport).value.run\n        result mustEqual Right(())\n      }\n\n    \"returns a SharedCollectionsException if the service throws an exception\" in\n      new SharedCollectionsProcessProcessScope {\n        mockApiServices.subscribe(any)(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.subscribe(sharedCollectionId)(contextSupport))\n      }\n\n    \"returns a SharedCollectionsException if the service throws an exception fetching the collections\" in\n      new SharedCollectionsProcessProcessScope {\n        mockApiServices.subscribe(any)(any) returns\n          TaskService(Task(Either.right((): Unit)))\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.subscribe(sharedCollectionId)(contextSupport))\n      }\n\n    \"returns a SharedCollectionsException if the service throws an exception updating the collection\" in\n      new SharedCollectionsProcessProcessScope {\n        mockApiServices.subscribe(any)(any) returns\n          TaskService(Task(Either.right((): Unit)))\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(any) returns\n          TaskService(Task(Either.right(Option(collection))))\n        mockPersistenceServices.updateCollection(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.subscribe(sharedCollectionId)(contextSupport))\n      }\n\n    \"returns a SharedCollectionsConfigurationException if the service throws a config exception\" in\n      new SharedCollectionsProcessProcessScope {\n        mockApiServices.subscribe(any)(any) returns\n          TaskService(Task(Either.left(apiConfigException)))\n\n        mustLeft[SharedCollectionsConfigurationException](\n          sharedCollectionsProcess.subscribe(sharedCollectionId)(contextSupport))\n      }\n  }\n\n  \"unsubscribe\" should {\n\n    \"returns a sequence of the subscriptions for a valid request\" in\n      new SharedCollectionsProcessProcessScope {\n        mockApiServices.unsubscribe(any)(any) returns\n          TaskService(Task(Either.right((): Unit)))\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(any) returns\n          TaskService(Task(Either.right(Option(collection))))\n        mockPersistenceServices.updateCollection(any) returns\n          TaskService(Task(Either.right(1)))\n\n        val result =\n          sharedCollectionsProcess.unsubscribe(sharedCollectionId)(contextSupport).value.run\n        result mustEqual Right(())\n      }\n\n    \"returns a SharedCollectionsException if the service throws an exception\" in\n      new SharedCollectionsProcessProcessScope {\n        mockApiServices.unsubscribe(any)(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.unsubscribe(sharedCollectionId)(contextSupport))\n      }\n\n    \"returns a SharedCollectionsException if the service throws an exception fetching the collections\" in\n      new SharedCollectionsProcessProcessScope {\n        mockApiServices.unsubscribe(any)(any) returns\n          TaskService(Task(Either.right((): Unit)))\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.unsubscribe(sharedCollectionId)(contextSupport))\n      }\n\n    \"returns a SharedCollectionsException if the service throws an exception updating the collection\" in\n      new SharedCollectionsProcessProcessScope {\n        mockApiServices.unsubscribe(any)(any) returns\n          TaskService(Task(Either.right((): Unit)))\n        mockPersistenceServices.fetchCollectionBySharedCollectionId(any) returns\n          TaskService(Task(Either.right(Option(collection))))\n        mockPersistenceServices.updateCollection(any) returns\n          TaskService(Task(Either.left(apiException)))\n\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.unsubscribe(sharedCollectionId)(contextSupport))\n      }\n\n    \"returns a SharedCollectionsConfigurationException if the service throws a config exception\" in\n      new SharedCollectionsProcessProcessScope {\n        mockApiServices.unsubscribe(any)(any) returns\n          TaskService(Task(Either.left(apiConfigException)))\n\n        mustLeft[SharedCollectionsConfigurationException](\n          sharedCollectionsProcess.unsubscribe(sharedCollectionId)(contextSupport))\n      }\n  }\n\n  \"updateViewSharedCollection\" should {\n\n    \"returns a valid response when the service returns a right response\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.updateViewShareCollection(any)(any) returns TaskService(\n          Task(Either.right((): Unit)))\n        val result = sharedCollectionsProcess\n          .updateViewSharedCollection(sharedCollectionId)(contextSupport)\n          .value\n          .run\n        result mustEqual Right(())\n      }\n\n    \"returns a SharedCollectionsException if the service throws an exception\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.updateViewShareCollection(any)(any) returns TaskService(\n          Task(Either.left(apiException)))\n        mustLeft[SharedCollectionsException](\n          sharedCollectionsProcess.updateViewSharedCollection(sharedCollectionId)(contextSupport))\n      }\n\n    \"returns a SharedCollectionsConfigurationException if the service throws a config exception\" in\n      new SharedCollectionsProcessProcessScope {\n\n        mockApiServices.updateViewShareCollection(any)(any) returns TaskService(\n          Task(Either.left(apiConfigException)))\n        mustLeft[SharedCollectionsConfigurationException](\n          sharedCollectionsProcess.updateViewSharedCollection(sharedCollectionId)(contextSupport))\n      }\n  }\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/theme/impl/ThemeProcessData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.theme.impl\n\nimport android.graphics.Color\nimport cards.nine.models.types.theme.ThemeLight\n\ntrait ThemeProcessData {\n\n  val nonExistingFileName        = \"nonExistingFile.json\"\n  val defaultThemeName           = \"theme name\"\n  val themeParentLight           = ThemeLight\n  val themeParentLightName       = \"light\"\n  val sampleColorWithAlpha       = \"#ff59afdd\"\n  val sampleColorWithoutAlpha    = \"#ffffff\"\n  val intSampleColorWithAlpha    = Color.parseColor(sampleColorWithAlpha)\n  val intSampleColorWithoutAlpha = Color.parseColor(sampleColorWithoutAlpha)\n\n  val validThemeJson =\n    s\"\"\"\n      |{\n      |  \"name\": \"$defaultThemeName\",\n      |  \"parent\": \"$themeParentLightName\",\n      |  \"styles\": [\n      |    {\n      |      \"styleType\": \"PrimaryColor\",\n      |      \"color\": \"$sampleColorWithAlpha\"\n      |    },\n      |    {\n      |      \"styleType\": \"DrawerTextColor\",\n      |      \"color\": \"$sampleColorWithoutAlpha\"\n      |    }\n      |  ],\n      |  \"themeColors\": {\n      |    \"defaultColor\": \"$sampleColorWithAlpha\",\n      |    \"colors\": [\"$sampleColorWithAlpha\", \"$sampleColorWithoutAlpha\"]\n      |  }\n      |}\n    \"\"\".stripMargin\n\n  val wrongThemeParentJson =\n    s\"\"\"\n       |{\n       |  \"name\": \"$defaultThemeName\",\n       |  \"parent\": \"unknownParent\",\n       |  \"styles\": [\n       |    {\n       |      \"styleType\": \"PrimaryColor\",\n       |      \"color\": \"#3F51B5\"\n       |    }\n       |  ]\n       |}\n    \"\"\".stripMargin\n\n  val wrongThemeStyleTypeJson =\n    \"\"\"\n      |{\n      |  \"name\": \"light\",\n      |  \"parent\": \"$themeParentLightName\",\n      |  \"styles\": [\n      |    {\n      |      \"styleType\": \"UnknowStyleType\",\n      |      \"color\": \"#ffffff\"\n      |    }\n      |  ]\n      |}\n    \"\"\".stripMargin\n\n  val wrongThemeStyleColorJson =\n    \"\"\"\n      |{\n      |  \"name\": \"light\",\n      |  \"parent\": \"$themeParentLightName\",\n      |  \"styles\": [\n      |    {\n      |      \"styleType\": \"PrimaryColor\",\n      |      \"color\": \"#ffff\"\n      |    }\n      |  ]\n      |}\n    \"\"\".stripMargin\n\n  val wrongThemeJson =\n    \"\"\"\n      |{\n      |  \"name\": \"light\"\n      |}\n    \"\"\".stripMargin\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/theme/impl/ThemeProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.theme.impl\n\nimport android.content.res.Resources\nimport android.util.DisplayMetrics\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.utils.{AssetException, FileUtils}\nimport cards.nine.models.types.theme.{DrawerTextColor, PrimaryColor}\nimport cards.nine.process.theme.ThemeException\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\nimport scala.util.Success\n\ntrait ThemeProcessSpecification extends Specification with Mockito {\n\n  val assetException = AssetException(\"\")\n\n  trait ThemeProcessScope extends Scope with ThemeProcessData {\n\n    val resources = mock[Resources]\n    resources.getDisplayMetrics returns mock[DisplayMetrics]\n\n    val mockContextSupport = mock[ContextSupport]\n    val mockFileUtils      = mock[FileUtils]\n\n    val themeProcess = new ThemeProcessImpl {\n      override val fileUtils = mockFileUtils\n    }\n  }\n}\n\nclass ThemeProcessImplSpec extends ThemeProcessSpecification {\n\n  \"getTheme\" should {\n\n    \"return a valid NineCardsTheme object for a valid request\" in\n      new ThemeProcessScope {\n\n        mockFileUtils.readFile(any)(any) returns Success(validThemeJson)\n        val result = themeProcess.getTheme(\"\")(mockContextSupport).value.run\n\n        result must beLike {\n          case Right(theme) =>\n            theme.name mustEqual defaultThemeName\n            theme.parent mustEqual themeParentLight\n            theme.get(PrimaryColor) mustEqual intSampleColorWithAlpha\n            theme.get(DrawerTextColor) mustEqual intSampleColorWithoutAlpha\n        }\n      }\n\n    \"return a ThemeException if the JSON is not valid\" in\n      new ThemeProcessScope {\n\n        mockFileUtils.readFile(any)(any) returns Success(wrongThemeJson)\n        val result = themeProcess.getTheme(\"\")(mockContextSupport).value.run\n        result must beAnInstanceOf[Left[ThemeException, _]]\n      }\n\n    \"return a ThemeException if a wrong parent is included in the JSON\" in\n      new ThemeProcessScope {\n\n        mockFileUtils.readFile(any)(any) returns Success(wrongThemeParentJson)\n        val result = themeProcess.getTheme(\"\")(mockContextSupport).value.run\n        result must beAnInstanceOf[Left[ThemeException, _]]\n      }\n\n    \"return a ThemeException if a wrong theme style type is included in the JSON\" in\n      new ThemeProcessScope {\n\n        mockFileUtils.readFile(any)(any) returns Success(wrongThemeStyleTypeJson)\n        val result = themeProcess.getTheme(\"\")(mockContextSupport).value.run\n        result must beAnInstanceOf[Left[ThemeException, _]]\n      }\n\n    \"return a ThemeException if a wrong theme style color is included in the JSON\" in\n      new ThemeProcessScope {\n\n        mockFileUtils.readFile(any)(any) returns Success(wrongThemeStyleColorJson)\n        val result = themeProcess.getTheme(\"\")(mockContextSupport).value.run\n        result must beAnInstanceOf[Left[ThemeException, _]]\n      }\n\n    \"return a AssetException if getJsonFromFile throws a exception\" in\n      new ThemeProcessScope {\n\n        mockFileUtils.readFile(any)(any) throws assetException\n        val result = themeProcess.getTheme(\"\")(mockContextSupport).value.run\n        result must beAnInstanceOf[Left[AssetException, _]]\n      }\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/trackevent/impl/AppDrawerTrackEventProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.commons.test.data.trackevent.AppDrawerTrackEventTestData\nimport cards.nine.process.trackevent.TrackEventException\nimport cards.nine.services.track.{TrackServices, TrackServicesException}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait AppDrawerTrackEventProcessSpecification\n    extends TaskServiceSpecification\n    with AppDrawerTrackEventTestData\n    with Mockito {\n\n  val trackServicesException = TrackServicesException(\"Irrelevant message\")\n\n  trait TrackServicesScope extends Scope {\n\n    val mockTrackServices = mock[TrackServices]\n\n    val process = new TrackEventProcessImpl(mockTrackServices)\n\n  }\n\n}\n\nclass AppDrawerTrackEventProcessImplSpec extends AppDrawerTrackEventProcessSpecification {\n\n  \"usingFastScroller\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.usingFastScroller().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(usingFastScrollerEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.usingFastScroller().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(usingFastScrollerEvent)\n    }\n\n  }\n\n  \"goToContacts\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.goToContacts().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(goToContactsEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.goToContacts().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(goToContactsEvent)\n    }\n\n  }\n\n  \"goToApps\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.goToApps().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(goToAppsEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.goToApps().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(goToAppsEvent)\n    }\n\n  }\n\n  \"addAppToCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.addAppToCollection(packageName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(addAppToCollectionEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.addAppToCollection(packageName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(addAppToCollectionEvent)\n    }\n\n  }\n\n  \"addContactToCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.addContactToCollection().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(addContactToCollectionEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.addContactToCollection().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(addContactToCollectionEvent)\n    }\n\n  }\n\n  \"goToGooglePlayButton\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.goToGooglePlayButton().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(goToGooglePlayButtonEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.goToGooglePlayButton().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(goToGooglePlayButtonEvent)\n    }\n\n  }\n\n  \"goToGoogleCallButton\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.goToGoogleCallButton().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(goToGoogleCallButtonEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.goToGoogleCallButton().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(goToGoogleCallButtonEvent)\n    }\n\n  }\n\n  \"goToFiltersByButton\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.goToFiltersByButton(filterName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(goToFiltersByButtonEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.goToFiltersByButton(filterName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(goToFiltersByButtonEvent)\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/trackevent/impl/CollectionDetailTrackEventProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.commons.test.data.trackevent.CollectionDetailTrackEventTestData\nimport cards.nine.process.trackevent.TrackEventException\nimport cards.nine.services.track.{TrackServices, TrackServicesException}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait CollectionDetailTrackEventProcessSpecification\n    extends TaskServiceSpecification\n    with CollectionDetailTrackEventTestData\n    with Mockito {\n\n  val trackServicesException = TrackServicesException(\"Irrelevant message\")\n\n  trait TrackServicesScope extends Scope {\n\n    val mockTrackServices = mock[TrackServices]\n\n    val process = new TrackEventProcessImpl(mockTrackServices)\n\n  }\n\n}\n\nclass CollectionDetailTrackEventProcessImplSpec\n    extends CollectionDetailTrackEventProcessSpecification {\n\n  \"useNavigationBar\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.useNavigationBar().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(useNavigationBarEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.useNavigationBar().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(useNavigationBarEvent)\n    }\n\n  }\n\n  \"reorderApplication\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.reorderApplication(newPosition).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(reorderApplicationEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.reorderApplication(newPosition).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(reorderApplicationEvent)\n    }\n\n  }\n\n  \"moveApplications\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.moveApplications(collectionName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(moveApplicationsEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.moveApplications(collectionName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(moveApplicationsEvent)\n    }\n\n  }\n\n  \"removeApplications\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.removeApplications(packageNameSeq).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(removeApplicationsEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.removeApplications(packageNameSeq).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(removeApplicationsEvent)\n    }\n\n  }\n\n  \"closeCollectionByGesture\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.closeCollectionByGesture().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(closeCollectionByGestureEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.closeCollectionByGesture().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(closeCollectionByGestureEvent)\n    }\n\n  }\n\n  \"addShortcutByFab\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.addShortcutByFab(shortcutName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(addShortcutByFabEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.addShortcutByFab(shortcutName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(addShortcutByFabEvent)\n    }\n\n  }\n\n  \"addRecommendationByFab\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.addRecommendationByFab(packageName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(addRecommendationByFabEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.addRecommendationByFab(packageName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(addRecommendationByFabEvent)\n    }\n\n  }\n\n  \"addContactByFab\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.addContactByFab().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(addContactByFabEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.addContactByFab().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(addContactByFabEvent)\n    }\n\n  }\n\n  \"addAppsByFab\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.addAppsByFab(packageNameSeq).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(addAppsByFabEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.addAppsByFab(packageNameSeq).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(addAppsByFabEvent)\n    }\n\n  }\n\n  \"removeAppsByFab\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.removeAppsByFab(packageNameSeq).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(removeAppsByFabEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.removeAppsByFab(packageNameSeq).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(removeAppsByFabEvent)\n    }\n\n  }\n\n  \"addCardByMenu\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.addCardByMenu().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(addCardByMenuEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.addCardByMenu().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(addCardByMenuEvent)\n    }\n\n  }\n\n  \"publishCollectionByMenu\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.publishCollectionByMenu(collectionName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(publishCollectionByMenuEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.publishCollectionByMenu(collectionName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(publishCollectionByMenuEvent)\n    }\n\n  }\n\n  \"shareCollectionAfterPublishing\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.shareCollectionAfterPublishing(sharedCollectionId).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(shareCollectionAfterPublishingEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.shareCollectionAfterPublishing(sharedCollectionId).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(shareCollectionAfterPublishingEvent)\n    }\n\n  }\n\n  \"shareCollectionByMenu\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.shareCollectionByMenu(sharedCollectionId).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(shareCollectionByMenuEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.shareCollectionByMenu(sharedCollectionId).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(shareCollectionByMenuEvent)\n    }\n\n  }\n\n  \"openAppFromCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.openAppFromCollection(entertainmentPackageName, entertainmentCategory).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(openAppFromCollectionEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process\n        .openAppFromCollection(entertainmentPackageName, entertainmentCategory)\n        .mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(openAppFromCollectionEvent)\n    }\n\n  }\n\n  \"addAppToCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.addAppToCollection(entertainmentPackageName, entertainmentCategory).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(addAppEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process\n        .addAppToCollection(entertainmentPackageName, entertainmentCategory)\n        .mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(addAppEvent)\n    }\n\n  }\n\n  \"removeFromCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.removeFromCollection(entertainmentPackageName, entertainmentCategory).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(removeEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process\n        .removeFromCollection(entertainmentPackageName, entertainmentCategory)\n        .mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(removeEvent)\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/trackevent/impl/HomeTrackEventProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.commons.test.data.trackevent.HomeTrackEventTestData\nimport cards.nine.process.trackevent.TrackEventException\nimport cards.nine.services.track.{TrackServices, TrackServicesException}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait HomeTrackEventProcessSpecification\n    extends TaskServiceSpecification\n    with HomeTrackEventTestData\n    with Mockito {\n\n  val trackServicesException = TrackServicesException(\"Irrelevant message\")\n\n  trait TrackServicesScope extends Scope {\n\n    val mockTrackServices = mock[TrackServices]\n\n    val process = new TrackEventProcessImpl(mockTrackServices)\n\n  }\n\n}\n\nclass HomeTrackEventProcessImplSpec extends HomeTrackEventProcessSpecification {\n\n  \"openCollectionTitle\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.openCollectionTitle(collectionName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(openCollectionTitleEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.openCollectionTitle(collectionName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(openCollectionTitleEvent)\n    }\n\n  }\n\n  \"openCollectionOrder\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.openCollectionOrder(position).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(openCollectionOrderEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.openCollectionOrder(position).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(openCollectionOrderEvent)\n    }\n\n  }\n\n  \"deleteCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.deleteCollection(collectionName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(deleteCollectionEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.deleteCollection(collectionName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(deleteCollectionEvent)\n    }\n\n  }\n\n  \"reorderCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.reorderCollection().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(reorderCollectionEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.reorderCollection().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(reorderCollectionEvent)\n    }\n\n  }\n\n  \"usingSearchByKeyboard\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.usingSearchByKeyboard().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(usingSearchByKeyboardEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.usingSearchByKeyboard().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(usingSearchByKeyboardEvent)\n    }\n\n  }\n\n  \"usingSearchByVoice\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.usingSearchByVoice().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(usingSearchByVoiceEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.usingSearchByVoice().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(usingSearchByVoiceEvent)\n    }\n\n  }\n\n  \"createNewCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.createNewCollection().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(createNewCollectionEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.createNewCollection().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(createNewCollectionEvent)\n    }\n\n  }\n\n  \"editCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.editCollection(collectionName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(editCollectionEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.editCollection(collectionName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(editCollectionEvent)\n    }\n\n  }\n\n  \"openMyCollections\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.openMyCollections().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(openMyCollectionsEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.openMyCollections().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(openMyCollectionsEvent)\n    }\n\n  }\n\n  \"openPublicCollections\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.openPublicCollections().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(openPublicCollectionsEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.openPublicCollections().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(openPublicCollectionsEvent)\n    }\n\n  }\n\n  \"createNewCollectionFromMyCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.createNewCollectionFromMyCollection(collectionName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(createNewCollectionFromMyCollectionEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.createNewCollectionFromMyCollection(collectionName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(createNewCollectionFromMyCollectionEvent)\n    }\n\n  }\n\n  \"createNewCollectionFromPublicCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.createNewCollectionFromPublicCollection(collectionName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(createNewCollectionFromPublicCollectionEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.createNewCollectionFromPublicCollection(collectionName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(createNewCollectionFromPublicCollectionEvent)\n    }\n\n  }\n\n  \"openDockAppTitle\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.openDockAppTitle(packageName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(openDockAppTitleEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.openDockAppTitle(packageName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(openDockAppTitleEvent)\n    }\n\n  }\n\n  \"openDockAppOrder\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.openDockAppOrder(position).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(openDockAppOrderEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.openDockAppOrder(position).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(openDockAppOrderEvent)\n    }\n\n  }\n\n  \"goToAppDrawer\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.goToAppDrawer().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(goToAppDrawerEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.goToAppDrawer().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(goToAppDrawerEvent)\n    }\n\n  }\n\n  \"appLinkReceived\" should {\n\n    \"track the app with the right parameters including a supported app link\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.appLinkReceived(supported).mustRightUnit\n\n      appLinkReceivedEvent\n    }\n\n    \"track the app with the right parameters including a not supported app link\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.appLinkReceived(notSupported).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(\n        appLinkReceivedEvent.copy(label = Option(notSupportedStr)))\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.appLinkReceived(supported).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(appLinkReceivedEvent)\n    }\n\n  }\n\n  \"sharedContentReceived\" should {\n\n    \"track the app with the right parameters including a supported shared content\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.sharedContentReceived(supported).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(sharedContentReceivedEvent)\n    }\n\n    \"track the app with the right parameters including a not supported shared content\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.sharedContentReceived(notSupported).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(\n        sharedContentReceivedEvent.copy(label = Option(notSupportedStr)))\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.sharedContentReceived(supported).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(sharedContentReceivedEvent)\n    }\n\n  }\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/trackevent/impl/LauncherTrackEventProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.commons.test.data.trackevent.LauncherTrackEventTestData\nimport cards.nine.models.types.{AppCategory, Game}\nimport cards.nine.process.trackevent.TrackEventException\nimport cards.nine.services.track.{TrackServices, TrackServicesException}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait LauncherTrackEventProcessSpecification\n    extends TaskServiceSpecification\n    with LauncherTrackEventTestData\n    with Mockito {\n\n  val trackServicesException = TrackServicesException(\"Irrelevant message\")\n\n  trait TrackServicesScope extends Scope {\n\n    val mockTrackServices = mock[TrackServices]\n\n    val process = new TrackEventProcessImpl(mockTrackServices)\n\n  }\n\n}\n\nclass LauncherTrackEventProcessImplSpec extends LauncherTrackEventProcessSpecification {\n\n  \"openAppFromAppDrawer\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.openAppFromAppDrawer(entertainmentPackageName, entertainmentCategory).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(openAppEntertainmentEvent)\n    }\n\n    \"track the app with the right parameters when the package is a game\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.openAppFromAppDrawer(gamePackageName, gameCategory).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(openAppGameEvent)\n      there was one(mockTrackServices).trackEvent(\n        openAppGameEvent.copy(category = AppCategory(Game)))\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process\n        .openAppFromAppDrawer(entertainmentPackageName, entertainmentCategory)\n        .mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(openAppEntertainmentEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception in the second call\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns (serviceRight(Unit), serviceLeft(\n        trackServicesException))\n\n      process.openAppFromAppDrawer(gamePackageName, gameCategory).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(openAppGameEvent)\n      there was one(mockTrackServices).trackEvent(\n        openAppGameEvent.copy(category = AppCategory(Game)))\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/trackevent/impl/MomentsTrackEventProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.commons.test.data.trackevent.MomentsTrackEventTestData\nimport cards.nine.process.trackevent.TrackEventException\nimport cards.nine.services.track.{TrackServices, TrackServicesException}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait MomentsTrackEventProcessSpecification\n    extends TaskServiceSpecification\n    with MomentsTrackEventTestData\n    with Mockito {\n\n  val trackServicesException = TrackServicesException(\"Irrelevant message\")\n\n  trait TrackServicesScope extends Scope {\n\n    val mockTrackServices = mock[TrackServices]\n\n    val process = new TrackEventProcessImpl(mockTrackServices)\n\n  }\n\n}\n\nclass MomentsTrackEventProcessImplSpec extends MomentsTrackEventProcessSpecification {\n\n  \"openApplicationByMoment\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.openApplicationByMoment(momentName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(openApplicationByMomentEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.openApplicationByMoment(momentName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(openApplicationByMomentEvent)\n    }\n\n  }\n\n  \"editMoment\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.editMoment(momentName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(editMomentEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.editMoment(momentName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(editMomentEvent)\n    }\n\n  }\n\n  \"changeMoment\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.changeMoment(momentName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(changeMomentEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.changeMoment(momentName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(changeMomentEvent)\n    }\n\n  }\n\n  \"addMoment\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.addMoment(momentName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(addMomentEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.addMoment(momentName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(addMomentEvent)\n    }\n\n  }\n\n  \"addWidget\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.addWidget(widgetName).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(addWidgetEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.addWidget(widgetName).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(addWidgetEvent)\n    }\n\n  }\n\n  \"unpinMoment\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.unpinMoment().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(unpinMomentEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.unpinMoment().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(unpinMomentEvent)\n    }\n\n  }\n\n  \"goToWeather\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.goToWeather().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(goToWeatherEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.goToWeather().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(goToWeatherEvent)\n    }\n\n  }\n\n  \"quickAccessToCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.quickAccessToCollection().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(quickAccessToCollectionEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.quickAccessToCollection().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(quickAccessToCollectionEvent)\n    }\n\n  }\n\n  \"setHours\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.setHours().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(setHoursEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.setHours().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(setHoursEvent)\n    }\n\n  }\n\n  \"setWifi\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.setWifi().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(setWifiEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.setWifi().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(setWifiEvent)\n    }\n\n  }\n\n  \"setBluetooth\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.setBluetooth().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(setBluetoothEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.setBluetooth().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(setBluetoothEvent)\n    }\n\n  }\n\n  \"deleteMoment\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.deleteMoment().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(deleteMomentEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.deleteMoment().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(deleteMomentEvent)\n    }\n\n  }\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/trackevent/impl/ProfileTrackEventProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.commons.test.data.trackevent.ProfileTrackEventTestData\nimport cards.nine.process.trackevent.TrackEventException\nimport cards.nine.services.track.{TrackServices, TrackServicesException}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait ProfileTrackEventProcessSpecification\n    extends TaskServiceSpecification\n    with ProfileTrackEventTestData\n    with Mockito {\n\n  val trackServicesException = TrackServicesException(\"Irrelevant message\")\n\n  trait TrackServicesScope extends Scope {\n\n    val mockTrackServices = mock[TrackServices]\n\n    val process = new TrackEventProcessImpl(mockTrackServices)\n\n  }\n\n}\n\nclass ProfileTrackEventProcessImplSpec extends ProfileTrackEventProcessSpecification {\n\n  \"logout\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.logout().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(logoutEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.logout().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(logoutEvent)\n    }\n\n  }\n\n  \"showAccountsContent\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.showAccountsContent().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(showAccountsContentEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.showAccountsContent().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(showAccountsContentEvent)\n    }\n\n  }\n\n  \"copyConfiguration\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.copyConfiguration().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(copyConfigurationEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.copyConfiguration().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(copyConfigurationEvent)\n    }\n\n  }\n\n  \"synchronizeConfiguration\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.synchronizeConfiguration().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(synchronizeConfigurationEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.synchronizeConfiguration().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(synchronizeConfigurationEvent)\n    }\n\n  }\n\n  \"changeConfigurationName\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.changeConfigurationName().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(changeConfigurationNameEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.changeConfigurationName().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(changeConfigurationNameEvent)\n    }\n\n  }\n\n  \"showPublicationsContent\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.showPublicationsContent().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(showPublicationsContentEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.showPublicationsContent().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(showPublicationsContentEvent)\n    }\n\n  }\n\n  \"addToMyCollectionsFromProfile\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.addToMyCollectionsFromProfile(publication).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(addToMyCollectionsFromProfileEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.addToMyCollectionsFromProfile(publication).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(addToMyCollectionsFromProfileEvent)\n    }\n\n  }\n\n  \"shareCollectionFromProfile\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.shareCollectionFromProfile(publication).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(shareCollectionFromProfileEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.shareCollectionFromProfile(publication).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(shareCollectionFromProfileEvent)\n    }\n\n  }\n\n  \"showSubscriptionsContent\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.showSubscriptionsContent().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(showSubscriptionsContentEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.showSubscriptionsContent().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(showSubscriptionsContentEvent)\n    }\n\n  }\n\n  \"subscribeToCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.subscribeToCollection(sharedCollectionId).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(subscribeToCollectionEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.subscribeToCollection(sharedCollectionId).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(subscribeToCollectionEvent)\n    }\n\n  }\n\n  \"unsubscribeFromCollection\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.unsubscribeFromCollection(sharedCollectionId).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(unsubscribeFromCollectionEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.unsubscribeFromCollection(sharedCollectionId).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(unsubscribeFromCollectionEvent)\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/trackevent/impl/SliderMenuTrackEventProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.trackevent.SliderMenuTrackEventTestData\nimport cards.nine.process.trackevent.TrackEventException\nimport cards.nine.services.track.{TrackServices, TrackServicesException}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait SliderMenuTrackEventProcessSpecification\n    extends TaskServiceSpecification\n    with SliderMenuTrackEventTestData\n    with Mockito {\n\n  val trackServicesException = TrackServicesException(\"Irrelevant message\")\n\n  trait TrackServicesScope extends Scope {\n\n    val mockTrackServices = mock[TrackServices]\n\n    val process = new TrackEventProcessImpl(mockTrackServices)\n\n  }\n\n}\n\nclass SliderMenuTrackEventProcessImplSpec extends SliderMenuTrackEventProcessSpecification {\n\n  \"goToCollectionsByMenu\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.goToCollectionsByMenu().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(goToCollectionsByMenuEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.goToCollectionsByMenu().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(goToCollectionsByMenuEvent)\n    }\n\n  }\n\n  \"goToMomentsByMenu\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.goToMomentsByMenu().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(goToMomentsByMenuEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.goToMomentsByMenu().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(goToMomentsByMenuEvent)\n    }\n\n  }\n\n  \"goToProfileByMenu\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.goToProfileByMenu().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(goToProfileByMenuEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.goToProfileByMenu().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(goToProfileByMenuEvent)\n    }\n\n  }\n\n  \"goToSendUsFeedback\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.goToSendUsFeedback().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(goToSendUsFeedbackEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.goToSendUsFeedback().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(goToSendUsFeedbackEvent)\n    }\n\n  }\n\n  \"goToHelpByMenu\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.goToHelpByMenu().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(goToHelpByMenuEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.goToHelpByMenu().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(goToHelpByMenuEvent)\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/trackevent/impl/WidgetTrackEventProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.TrackEventValues._\nimport cards.nine.commons.test.data.trackevent.WidgetTrackEventTestData\nimport cards.nine.process.trackevent.TrackEventException\nimport cards.nine.services.track.{TrackServices, TrackServicesException}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait WidgetTrackEventProcessSpecification\n    extends TaskServiceSpecification\n    with WidgetTrackEventTestData\n    with Mockito {\n\n  val trackServicesException = TrackServicesException(\"Irrelevant message\")\n\n  trait TrackServicesScope extends Scope {\n\n    val mockTrackServices = mock[TrackServices]\n\n    val process = new TrackEventProcessImpl(mockTrackServices)\n\n  }\n\n}\n\nclass WidgetTrackEventProcessImplSpec extends WidgetTrackEventProcessSpecification {\n\n  \"addWidgetToMoment\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.addWidgetToMoment(momentPackageName, momentClassName, momentCategory).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(momentEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process\n        .addWidgetToMoment(momentPackageName, momentClassName, momentCategory)\n        .mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(momentEvent)\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/trackevent/impl/WizardTrackEventProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.trackevent.impl\n\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.data.trackevent.WizardTrackEventTestData\nimport cards.nine.models.types._\nimport cards.nine.process.trackevent.TrackEventException\nimport cards.nine.services.track.{TrackServices, TrackServicesException}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait WizardTrackEventProcessSpecification\n    extends TaskServiceSpecification\n    with WizardTrackEventTestData\n    with Mockito {\n\n  val trackServicesException = TrackServicesException(\"Irrelevant message\")\n\n  trait TrackServicesScope extends Scope {\n\n    val mockTrackServices = mock[TrackServices]\n\n    val process = new TrackEventProcessImpl(mockTrackServices)\n\n  }\n\n}\n\nclass WizardTrackEventProcessImplSpec extends WizardTrackEventProcessSpecification {\n\n  \"chooseAccount\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.chooseAccount().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(chooseAccountEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.chooseAccount().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(chooseAccountEvent)\n    }\n\n  }\n\n  \"chooseNewConfiguration\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.chooseNewConfiguration().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(chooseNewConfigurationEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.chooseNewConfiguration().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(chooseNewConfigurationEvent)\n    }\n\n  }\n\n  \"chooseExistingDevice\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.chooseExistingDevice().mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(chooseExistingDeviceEvent)\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.chooseExistingDevice().mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(chooseExistingDeviceEvent)\n    }\n\n  }\n\n  \"chooseMoment\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.chooseMoment(OutAndAboutMoment).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(chooseMomentEvent)\n    }\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.chooseMoment(HomeMorningMoment).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(chooseMomentEvent.copy(label = Some(\"HOME\")))\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.chooseMoment(OutAndAboutMoment).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(chooseMomentEvent)\n    }\n\n  }\n\n  \"chooseMomentWifi\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.chooseMomentWifi(OutAndAboutMoment).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(chooseMomentWifiEvent)\n    }\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.chooseMomentWifi(HomeNightMoment).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(\n        chooseMomentWifiEvent.copy(label = Some(\"NIGHT\")))\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.chooseMomentWifi(OutAndAboutMoment).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(chooseMomentWifiEvent)\n    }\n\n  }\n\n  \"chooseOtherMoment\" should {\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.chooseOtherMoment(MusicMoment).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(chooseOtherMomentEvent)\n    }\n\n    \"track the app with the right parameters\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceRight(Unit)\n\n      process.chooseOtherMoment(CarMoment).mustRightUnit\n\n      there was one(mockTrackServices).trackEvent(chooseOtherMomentEvent.copy(label = Some(\"CAR\")))\n    }\n\n    \"return a Left[TrackEventException] when the service return an exception\" in new TrackServicesScope {\n\n      mockTrackServices.trackEvent(any) returns serviceLeft(trackServicesException)\n\n      process.chooseOtherMoment(MusicMoment).mustLeft[TrackEventException]\n\n      there was one(mockTrackServices).trackEvent(chooseOtherMomentEvent)\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/user/impl/UserProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.user.impl\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.{ApiTestData, UserTestData}\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.process.user.UserException\nimport cards.nine.services.api._\nimport cards.nine.services.persistence._\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait UserProcessSpecification extends Specification with Mockito {\n\n  trait UserProcessScope extends Scope with UserTestData with ApiTestData {\n\n    val mockContextSupport = mock[ContextSupport]\n\n    val mockApiServices = mock[ApiServices]\n\n    val mockPersistenceServices = mock[PersistenceServices]\n\n    val userProcess = new UserProcessImpl(mockApiServices, mockPersistenceServices)\n\n  }\n\n}\n\nclass UserProcessImplSpec extends UserProcessSpecification {\n\n  \"Sign In in UserProcess\" should {\n\n    \"returns a UserException if there is no active user\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns None\n\n        val result =\n          userProcess.signIn(email, marketToken, emailTokenId)(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).login(any, any, any)\n        there was no(mockPersistenceServices).findUserById(any)\n        there was no(mockPersistenceServices).updateUser(any)\n        result must beAnInstanceOf[Left[UserException, _]]\n      }\n\n    \"returns a UserException if the user doesn't exists in the database\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.getAndroidId(any) returns TaskService(\n          Task(Either.right(androidId)))\n        mockApiServices.login(any, any, any) returns TaskService(Task(Either.right(loginResponse)))\n        mockPersistenceServices.findUserById(any) returns TaskService(Task(Either.right(None)))\n\n        val result =\n          userProcess.signIn(email, marketToken, emailTokenId)(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).getAndroidId(mockContextSupport)\n        there was one(mockApiServices).login(email, androidId, emailTokenId)\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was no(mockPersistenceServices).updateUser(any)\n\n        result must beAnInstanceOf[Left[UserException, _]]\n      }\n\n    \"returns Unit when all services work fine\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.getAndroidId(any) returns TaskService(\n          Task(Either.right(androidId)))\n        mockApiServices.login(any, any, any) returns TaskService(Task(Either.right(loginResponse)))\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(user))))\n        mockPersistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n\n        val result =\n          userProcess.signIn(email, marketToken, emailTokenId)(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).getAndroidId(mockContextSupport)\n        there was one(mockApiServices).login(email, androidId, emailTokenId)\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was one(mockPersistenceServices).updateUser(user)\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n  }\n\n  \"Register in UserProcess\" should {\n\n    \"register as active the first user return by fetchUsers service when there is no active user id\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns None\n        mockPersistenceServices.fetchUsers returns TaskService(\n          Task(Either.right(Seq(user, anotherUser))))\n\n        val result = userProcess.register(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).fetchUsers\n        there was one(mockContextSupport).setActiveUserId(user.id)\n        there was no(mockPersistenceServices).findUserById(any)\n        there was no(mockPersistenceServices).addUser(any)\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n    \"register as active a new user when fetchUsers return an empty seq and there is no active user id\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns None\n        mockPersistenceServices.fetchUsers returns TaskService(Task(Either.right(Seq.empty)))\n        mockPersistenceServices.addUser(any) returns TaskService(Task(Either.right(user)))\n\n        val result = userProcess.register(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).fetchUsers\n        there was one(mockContextSupport).setActiveUserId(user.id)\n        there was one(mockPersistenceServices).addUser(emptyUserData)\n        there was no(mockPersistenceServices).findUserById(any)\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n    \"not register an active user when there is one active user id and it exists in the database\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(user))))\n\n        val result = userProcess.register(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was no(mockContextSupport).setActiveUserId(user.id)\n        there was no(mockPersistenceServices).addUser(emptyUserData)\n        there was no(mockPersistenceServices).fetchUsers\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n    \"register as active a new user when there is one active user id but it doesn't exists in the database\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(newUserId)\n        mockPersistenceServices.findUserById(any) returns TaskService(Task(Either.right(None)))\n        mockPersistenceServices.addUser(any) returns TaskService(Task(Either.right(user)))\n\n        val result = userProcess.register(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(newUserId)\n        there was one(mockContextSupport).setActiveUserId(user.id)\n        there was one(mockPersistenceServices).addUser(emptyUserData)\n        there was no(mockPersistenceServices).fetchUsers\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n  }\n\n  \"Unregister in UserProcess\" should {\n\n    \"returns a UserException if there is no active user\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns None\n\n        val result = userProcess.unregister(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was no(mockPersistenceServices).findUserById(any)\n        there was no(mockPersistenceServices).updateUser(any)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Left[UserException, _]]\n      }\n\n    \"returns a UserException if the user doesn't exists in the database\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(Task(Either.right(None)))\n\n        val result = userProcess.unregister(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was no(mockPersistenceServices).updateUser(any)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Left[UserException, _]]\n      }\n\n    \"update the user in the database but don't call to update installation if the user doesn't have a device token\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        val userToUpdate =\n          user.copy(apiKey = Some(apiKey), sessionToken = Some(sessionToken), deviceToken = None)\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(userToUpdate))))\n        mockPersistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n\n        val result = userProcess.unregister(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was one(mockPersistenceServices).updateUser(emptyUser)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n    \"update the user in the database but don't call to update installation if the user doesn't have an api key\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        val userToUpdate = user\n          .copy(apiKey = None, sessionToken = Some(sessionToken), deviceToken = Some(deviceToken))\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(userToUpdate))))\n        mockPersistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n\n        val result = userProcess.unregister(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was one(mockPersistenceServices).updateUser(emptyUser)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n    \"update the user in the database but don't call to update installation if the user doesn't have a session token\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        val userToUpdate =\n          user.copy(apiKey = Some(apiKey), sessionToken = None, deviceToken = Some(deviceToken))\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(userToUpdate))))\n        mockPersistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n\n        val result = userProcess.unregister(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was one(mockPersistenceServices).updateUser(emptyUser)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n    \"update the user in the database and call to update installation with None if the user has a device token, api key and session token\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        val userToUpdate = user.copy(\n          apiKey = Some(apiKey),\n          sessionToken = Some(sessionToken),\n          deviceToken = Some(deviceToken))\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(userToUpdate))))\n        mockPersistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n        mockPersistenceServices.getAndroidId(any) returns TaskService(\n          Task(Either.right(androidId)))\n        mockApiServices.updateInstallation(any)(any) returns TaskService(\n          Task(Either.right((): Unit)))\n\n        val result = userProcess.unregister(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was one(mockPersistenceServices).updateUser(emptyUser)\n        there was no(mockPersistenceServices).getAndroidId(mockContextSupport)\n        there was no(mockApiServices).updateInstallation(None)(requestConfig)\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n  }\n\n  \"Get User in UserProcess\" should {\n\n    \"returns a UserException if there is no active user\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns None\n\n        val result = userProcess.getUser(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was no(mockPersistenceServices).findUserById(any)\n\n        result must beAnInstanceOf[Left[UserException, _]]\n      }\n\n    \"returns a UserException if the user doesn't exists in the database\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(Task(Either.right(None)))\n\n        val result = userProcess.getUser(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n\n        result must beAnInstanceOf[Left[UserException, _]]\n      }\n\n    \"returns the user that exists in the database\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(user))))\n\n        val result = userProcess.getUser(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n\n        result shouldEqual Right(user)\n      }\n  }\n\n  \"Update User Device in UserProcess\" should {\n\n    \"returns a UserException if there is no active user\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns None\n\n        val result = userProcess\n          .updateUserDevice(userDeviceName, deviceCloudId, Some(deviceToken))(mockContextSupport)\n          .value\n          .run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was no(mockPersistenceServices).findUserById(any)\n        there was no(mockPersistenceServices).updateUser(any)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Left[UserException, _]]\n      }\n\n    \"returns a UserException if the user doesn't exists in the database\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(Task(Either.right(None)))\n\n        val result = userProcess\n          .updateUserDevice(userDeviceName, deviceCloudId, Some(deviceToken))(mockContextSupport)\n          .value\n          .run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was no(mockPersistenceServices).updateUser(any)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Left[UserException, _]]\n      }\n\n    \"updates the user in the database with the new data but doesn't call to update installation when the user doesn't have api key\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        val userToUpdate = user\n          .copy(apiKey = None, sessionToken = Some(sessionToken), deviceToken = Some(deviceToken))\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(userToUpdate))))\n        mockPersistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n\n        val result = userProcess\n          .updateUserDevice(anotherUserDeviceName, anotherDeviceCloudId, Some(anotherDeviceToken))(\n            mockContextSupport)\n          .value\n          .run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        val userUpdated = userToUpdate.copy(\n          deviceName = Some(anotherUserDeviceName),\n          deviceCloudId = Some(anotherDeviceCloudId),\n          deviceToken = Some(anotherDeviceToken))\n        there was one(mockPersistenceServices).updateUser(userUpdated)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n    \"updates the user in the database with the new data but doesn't call to update installation when the user doesn't have session key\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        val userToUpdate =\n          user.copy(apiKey = Some(apiKey), sessionToken = None, deviceToken = Some(deviceToken))\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(userToUpdate))))\n        mockPersistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n\n        val result = userProcess\n          .updateUserDevice(anotherUserDeviceName, anotherDeviceCloudId, Some(anotherDeviceToken))(\n            mockContextSupport)\n          .value\n          .run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        val userUpdated = userToUpdate.copy(\n          deviceName = Some(anotherUserDeviceName),\n          deviceCloudId = Some(anotherDeviceCloudId),\n          deviceToken = Some(anotherDeviceToken))\n        there was one(mockPersistenceServices).updateUser(userUpdated)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n    \"updates the user in the database with the new data and calls to update installation when the user have an api key, a session key and the device token is different\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        val userToUpdate = user.copy(\n          apiKey = Some(apiKey),\n          sessionToken = Some(sessionToken),\n          deviceToken = Some(deviceToken))\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(userToUpdate))))\n        mockPersistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n        mockPersistenceServices.getAndroidId(any) returns TaskService(\n          Task(Either.right(androidId)))\n        mockApiServices.updateInstallation(any)(any) returns TaskService(\n          Task(Either.right((): Unit)))\n\n        val result = userProcess\n          .updateUserDevice(anotherUserDeviceName, anotherDeviceCloudId, Some(anotherDeviceToken))(\n            mockContextSupport)\n          .value\n          .run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        val userUpdated = userToUpdate.copy(\n          deviceName = Some(anotherUserDeviceName),\n          deviceCloudId = Some(anotherDeviceCloudId),\n          deviceToken = Some(anotherDeviceToken))\n        there was one(mockPersistenceServices).updateUser(userUpdated)\n        there was one(mockPersistenceServices).getAndroidId(mockContextSupport)\n        there was one(mockApiServices).updateInstallation(Some(anotherDeviceToken))(\n          requestConfig.copy(marketToken = None))\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n    \"updates the user in the database with the new data and calls to update installation passing the user device token when is called with no device token\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        val userToUpdate = user.copy(\n          apiKey = Some(apiKey),\n          sessionToken = Some(sessionToken),\n          deviceToken = Some(deviceToken))\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(userToUpdate))))\n        mockPersistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n        mockPersistenceServices.getAndroidId(any) returns TaskService(\n          Task(Either.right(androidId)))\n        mockApiServices.updateInstallation(any)(any) returns TaskService(\n          Task(Either.right((): Unit)))\n\n        val result = userProcess\n          .updateUserDevice(anotherUserDeviceName, anotherDeviceCloudId, None)(mockContextSupport)\n          .value\n          .run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        val userUpdated = userToUpdate.copy(\n          deviceName = Some(anotherUserDeviceName),\n          deviceCloudId = Some(anotherDeviceCloudId))\n        there was one(mockPersistenceServices).updateUser(userUpdated)\n        there was one(mockPersistenceServices).getAndroidId(mockContextSupport)\n        there was one(mockApiServices).updateInstallation(Some(deviceToken))(\n          requestConfig.copy(marketToken = None))\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n  }\n\n  \"Update Device token in UserProcess\" should {\n\n    \"returns a UserException if there is no active user\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns None\n\n        val result = userProcess.updateDeviceToken(deviceToken)(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was no(mockPersistenceServices).findUserById(any)\n        there was no(mockPersistenceServices).updateUser(any)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Left[UserException, _]]\n      }\n\n    \"returns a UserException if the user doesn't exists in the database\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(Task(Either.right(None)))\n\n        val result = userProcess.updateDeviceToken(deviceToken)(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was no(mockPersistenceServices).updateUser(any)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Left[UserException, _]]\n      }\n\n    \"updates the user in the database with the new data but doesn't call to update installation when the user doesn't have an api key\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        val userToUpdate = user\n          .copy(apiKey = None, sessionToken = Some(sessionToken), deviceToken = Some(deviceToken))\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(userToUpdate))))\n        mockPersistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n\n        val result =\n          userProcess.updateDeviceToken(anotherDeviceToken)(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        val userUpdated = userToUpdate.copy(deviceToken = Some(anotherDeviceToken))\n        there was one(mockPersistenceServices).updateUser(userUpdated)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n    \"updates the user in the database with the new data but doesn't call to update installation when the user doesn't have session key\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        val userToUpdate =\n          user.copy(apiKey = Some(apiKey), sessionToken = None, deviceToken = Some(deviceToken))\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(userToUpdate))))\n        mockPersistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n\n        val result =\n          userProcess.updateDeviceToken(anotherDeviceToken)(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        val userUpdated = userToUpdate.copy(deviceToken = Some(anotherDeviceToken))\n        there was one(mockPersistenceServices).updateUser(userUpdated)\n        there was no(mockPersistenceServices).getAndroidId(any)\n        there was no(mockApiServices).updateInstallation(any)(any)\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n\n    \"updates the user in the database with the new data and calls to update installation when the user have an api key, a session key and the device token is different\" in\n      new UserProcessScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        val userToUpdate = user.copy(\n          apiKey = Some(apiKey),\n          sessionToken = Some(sessionToken),\n          deviceToken = Some(deviceToken))\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(userToUpdate))))\n        mockPersistenceServices.updateUser(any) returns TaskService(Task(Either.right(1)))\n        mockPersistenceServices.getAndroidId(any) returns TaskService(\n          Task(Either.right(androidId)))\n        mockApiServices.updateInstallation(any)(any) returns TaskService(\n          Task(Either.right((): Unit)))\n\n        val result =\n          userProcess.updateDeviceToken(anotherDeviceToken)(mockContextSupport).value.run\n\n        there was one(mockContextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        val userUpdated = userToUpdate.copy(deviceToken = Some(anotherDeviceToken))\n        there was one(mockPersistenceServices).updateUser(userUpdated)\n        there was one(mockPersistenceServices).getAndroidId(mockContextSupport)\n        there was one(mockApiServices).updateInstallation(Some(anotherDeviceToken))(\n          requestConfig.copy(marketToken = None))\n\n        result must beAnInstanceOf[Right[_, Unit]]\n      }\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/userv1/impl/UserV1ProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.userv1.impl\n\nimport android.content.pm.PackageManager\nimport android.content.res.Resources\nimport android.util.DisplayMetrics\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.{ApiV1TestData, UserTestData}\nimport cards.nine.commons.test.data.ApiV1Values._\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.process.userv1.UserV1Exception\nimport cards.nine.services.api.{ApiServiceException, ApiServices}\nimport cards.nine.services.persistence.PersistenceServices\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait UserV1ProcessSpecification extends Specification with Mockito {\n\n  val apiServiceException = ApiServiceException(\"\")\n\n  trait UserV1ProcessScope extends Scope with UserTestData with ApiV1TestData {\n\n    val resources = mock[Resources]\n    resources.getDisplayMetrics returns mock[DisplayMetrics]\n\n    val contextSupport = mock[ContextSupport]\n    contextSupport.getPackageManager returns mock[PackageManager]\n    contextSupport.getResources returns resources\n\n    val mockApiServices = mock[ApiServices]\n\n    val mockPersistenceServices = mock[PersistenceServices]\n    mockPersistenceServices.getAndroidId(any) returns TaskService(\n      Task(Either.right(userDeviceIdDefault)))\n\n    val userConfigProcess = new UserV1ProcessImpl(mockApiServices, mockPersistenceServices)\n\n  }\n\n}\n\nclass UserV1ProcessImplSpec extends UserV1ProcessSpecification {\n\n  \"Get UserInfo in UserConfigProcess\" should {\n\n    \"returns a UserConfigException if there is no active user\" in\n      new UserV1ProcessScope {\n\n        contextSupport.getActiveUserId returns None\n        val result =\n          userConfigProcess.getUserInfo(userDeviceName, permissions)(contextSupport).value.run\n        result must beAnInstanceOf[Left[UserV1Exception, _]]\n\n        there was one(contextSupport).getActiveUserId\n        there was no(mockPersistenceServices).findUserById(any)\n        there was no(mockApiServices).loginV1(any, any)\n        there was no(mockApiServices).getUserConfigV1()(any)\n\n      }\n\n    \"returns a UserConfigException if the user doesn't exists in the database\" in\n      new UserV1ProcessScope {\n\n        contextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(Task(Either.right(None)))\n\n        val result =\n          userConfigProcess.getUserInfo(userDeviceName, permissions)(contextSupport).value.run\n        result must beAnInstanceOf[Left[UserV1Exception, _]]\n\n        there was one(contextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was no(mockApiServices).loginV1(any, any)\n        there was no(mockApiServices).getUserConfigV1()(any)\n\n      }\n\n    \"returns a UserConfigException if the user doesn't have an email\" in\n      new UserV1ProcessScope {\n\n        contextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(user.copy(email = None)))))\n\n        val result =\n          userConfigProcess.getUserInfo(userDeviceName, permissions)(contextSupport).value.run\n        result must beAnInstanceOf[Left[UserV1Exception, _]]\n\n        there was one(contextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was no(mockApiServices).loginV1(any, any)\n        there was no(mockApiServices).getUserConfigV1()(any)\n\n      }\n\n    \"returns a UserConfigException if the user doesn't have a market token\" in\n      new UserV1ProcessScope {\n\n        contextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(user.copy(marketToken = None)))))\n\n        val result =\n          userConfigProcess.getUserInfo(userDeviceName, permissions)(contextSupport).value.run\n        result must beAnInstanceOf[Left[UserV1Exception, _]]\n\n        there was one(contextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was no(mockApiServices).loginV1(any, any)\n        there was no(mockApiServices).getUserConfigV1()(any)\n\n      }\n\n    \"returns a UserConfigException when the login doesn't return a session token\" in\n      new UserV1ProcessScope {\n\n        contextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(user))))\n        mockApiServices.loginV1(any, any) returns TaskService(\n          Task(Either.right(loginResponseV1.copy(sessionToken = None))))\n\n        val result = userConfigProcess\n          .getUserInfo(userDeviceNameDefault, permissions)(contextSupport)\n          .value\n          .run\n        result must beAnInstanceOf[Left[UserV1Exception, _]]\n\n        there was one(contextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was one(mockApiServices).loginV1(email, device)\n        there was no(mockApiServices).getUserConfigV1()(any)\n\n      }\n\n    \"returns a right response when all services work fine\" in\n      new UserV1ProcessScope {\n\n        contextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(user))))\n        mockApiServices.loginV1(any, any) returns TaskService(Task(Either.right(loginResponseV1)))\n        mockApiServices.getUserConfigV1()(any) returns TaskService(Task(Either.right(userV1)))\n\n        val result = userConfigProcess\n          .getUserInfo(userDeviceNameDefault, permissions)(contextSupport)\n          .value\n          .run\n        result must beLike {\n          case Right(userInfo) =>\n            userInfo.devices.length shouldEqual userV1.devices.length\n            userInfo.devices map (_.deviceName) shouldEqual userV1.devices.map(_.deviceName)\n        }\n\n        there was one(contextSupport).getActiveUserId\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was one(mockApiServices).loginV1(email, device)\n        there was one(mockApiServices).getUserConfigV1()(requestConfigV1)\n\n      }\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/utils/ApiUtilsSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.utils\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.UserTestData\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.services.api.ApiServiceException\nimport cards.nine.services.persistence.{\n  AndroidIdNotFoundException,\n  PersistenceServiceException,\n  PersistenceServices\n}\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait ApiUtilsSpecification extends Specification with Mockito with UserTestData {\n\n  val persistenceServicesException = PersistenceServiceException(\"\")\n  val androidIdNotFoundException   = AndroidIdNotFoundException(\"\")\n\n  trait ApiUtilsScope extends Scope {\n\n    val mockContextSupport      = mock[ContextSupport]\n    val mockPersistenceServices = mock[PersistenceServices]\n    val apiUtils                = new ApiUtils(mockPersistenceServices)\n\n  }\n\n}\n\nclass ApiUtilsSpec extends ApiUtilsSpecification {\n\n  \"Api Utils\" should {\n\n    \"returns an ApiServiceException when there ins't any active user\" in\n      new ApiUtilsScope {\n\n        mockContextSupport.getActiveUserId returns None\n        val result = apiUtils.getRequestConfig(mockContextSupport).value.run\n        result must beAnInstanceOf[Left[ApiServiceException, _]]\n      }\n\n    \"returns an ApiServiceException when there is an active user but doesn't exists in the database\" in\n      new ApiUtilsScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(Task(Either.right(None)))\n\n        val result = apiUtils.getRequestConfig(mockContextSupport).value.run\n        result must beAnInstanceOf[Left[ApiServiceException, _]]\n\n        there was one(mockPersistenceServices).findUserById(userId)\n      }\n\n    \"returns an ApiServiceException when there is an active in the database but doesn't have api key\" in\n      new ApiUtilsScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(user.copy(apiKey = None)))))\n\n        val result = apiUtils.getRequestConfig(mockContextSupport).value.run\n        result must beAnInstanceOf[Left[ApiServiceException, _]]\n\n        there was one(mockPersistenceServices).findUserById(userId)\n      }\n\n    \"returns an ApiServiceException when there is an active in the database but doesn't have a session token\" in\n      new ApiUtilsScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(user.copy(sessionToken = None)))))\n\n        val result = apiUtils.getRequestConfig(mockContextSupport).value.run\n        result must beAnInstanceOf[Left[ApiServiceException, _]]\n\n        there was one(mockPersistenceServices).findUserById(userId)\n      }\n\n    \"returns a request config with the correct data\" in\n      new ApiUtilsScope {\n\n        mockContextSupport.getActiveUserId returns Some(userId)\n        mockPersistenceServices.findUserById(any) returns TaskService(\n          Task(Either.right(Some(user))))\n        mockPersistenceServices.getAndroidId(any) returns TaskService(\n          Task(Either.right(androidId)))\n\n        val result = apiUtils.getRequestConfig(mockContextSupport).value.run\n        result must beLike {\n          case Right(resultRequestConfig) =>\n            resultRequestConfig.apiKey shouldEqual apiKey\n            resultRequestConfig.sessionToken shouldEqual sessionToken\n            resultRequestConfig.androidId shouldEqual androidId\n            resultRequestConfig.marketToken shouldEqual Some(marketToken)\n        }\n\n        there was one(mockPersistenceServices).findUserById(userId)\n        there was one(mockPersistenceServices).getAndroidId(mockContextSupport)\n      }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/process/src/test/scala/cards/nine/process/widget/impl/WidgetProcessImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.process.widget.impl\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.WidgetTestData\nimport cards.nine.commons.test.data.WidgetValues._\nimport cards.nine.models.Widget\nimport cards.nine.process.widget.AppWidgetException\nimport cards.nine.services.persistence.{PersistenceServiceException, PersistenceServices}\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait WidgetProcessImplSpecification extends Specification with Mockito {\n\n  val persistenceServiceException = PersistenceServiceException(\"\")\n\n  trait WidgetProcessScope extends Scope {\n\n    val mockPersistenceServices = mock[PersistenceServices]\n\n    val widgetProcess = new WidgetProcessImpl(persistenceServices = mockPersistenceServices)\n\n  }\n\n}\n\nclass WidgetProcessImplSpec extends WidgetProcessImplSpecification with WidgetTestData {\n\n  \"getWidgets\" should {\n\n    \"returns a sequence of widgets for a valid request\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.fetchWidgets returns TaskService(Task(Either.right(seqWidget)))\n        mockPersistenceServices.addWidgets(any) returns TaskService(Task(Either.right(widgets)))\n\n        val widgets: Seq[Widget] = Seq.empty\n\n        val result = widgetProcess.getWidgets.value.run\n        result must beLike {\n          case Right(resultSeqWidget) =>\n            resultSeqWidget.size shouldEqual seqWidget.size\n            resultSeqWidget map (_.packageName) shouldEqual seqWidget.map(_.packageName)\n        }\n      }\n\n    \"returns a WidgetException if the service throws a exception\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.fetchWidgets returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        mockPersistenceServices.addWidgets(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.getWidgets.value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n  }\n\n  \"getWidgetById\" should {\n\n    \"returns a widget for a valid request\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(widgetId) returns TaskService(\n          Task(Either.right(Option(widget))))\n        val result = widgetProcess.getWidgetById(widgetId).value.run\n        result must beLike {\n          case Right(resultWidget) =>\n            resultWidget must beSome.which { widget =>\n              widget.packageName shouldEqual widget.packageName\n            }\n        }\n      }\n\n    \"returns None for a valid request if the widgetId doesn't exist\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(nonExistentWidgetId) returns TaskService(\n          Task(Either.right(None)))\n        val result = widgetProcess.getWidgetById(nonExistentWidgetId).value.run\n        result shouldEqual Right(None)\n      }\n\n    \"returns a WidgetException if the service throws a exception\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(widgetId) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.getWidgetById(widgetId).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n  }\n\n  \"getWidgetByAppWidgetId\" should {\n\n    \"returns a widget for a valid request\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.fetchWidgetByAppWidgetId(appWidgetId) returns TaskService(\n          Task(Either.right(Option(widget))))\n        val result = widgetProcess.getWidgetByAppWidgetId(appWidgetId).value.run\n        result must beLike {\n          case Right(resultWidget) =>\n            resultWidget must beSome.which { widget =>\n              widget.packageName shouldEqual widget.packageName\n            }\n        }\n      }\n\n    \"returns None for a valid request if the appWidgetId doesn't exist\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.fetchWidgetByAppWidgetId(nonExistentAppWidgetId) returns TaskService(\n          Task(Either.right(None)))\n        val result = widgetProcess.getWidgetByAppWidgetId(nonExistentAppWidgetId).value.run\n        result shouldEqual Right(None)\n      }\n\n    \"returns a WidgetException if the service throws a exception\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.fetchWidgetByAppWidgetId(appWidgetId) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.getWidgetByAppWidgetId(appWidgetId).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n  }\n\n  \"getWidgetsByMoment\" should {\n\n    \"returns a sequence of widgets for a valid request\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.fetchWidgetsByMoment(widgetMomentId) returns\n          TaskService(Task(Either.right(seqWidget)))\n        val result = widgetProcess.getWidgetsByMoment(widgetMomentId).value.run\n        result must beLike {\n          case Right(resultWidgets) =>\n            resultWidgets map (_.packageName) shouldEqual (seqWidget map (_.packageName))\n        }\n      }\n\n    \"returns None for a valid request if the momentId doesn't exist\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.fetchWidgetsByMoment(nonExistentWidgetMomentId) returns TaskService(\n          Task(Either.right(Seq.empty)))\n        val result = widgetProcess.getWidgetsByMoment(nonExistentWidgetMomentId).value.run\n        result shouldEqual Right(Seq.empty)\n      }\n\n    \"returns a WidgetException if the service throws a exception\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.fetchWidgetsByMoment(widgetMomentId) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.getWidgetsByMoment(widgetMomentId).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n  }\n\n  \"addWidget\" should {\n\n    \"returns a the widget added for a valid request\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.addWidget(any) returns TaskService(Task(Either.right(widget)))\n        val result = widgetProcess.addWidget(widgetData).value.run\n        result shouldEqual Right(widget)\n      }\n\n    \"returns a WidgetException if the service throws a exception adding the new widget\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.addWidget(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.addWidget(widgetData).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n  }\n\n  \"addWidgets\" should {\n\n    \"returns the widgets added for a valid request\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.addWidgets(any) returns TaskService(Task(Either.right(seqWidget)))\n        val result = widgetProcess.addWidgets(seqWidgetData).value.run\n        result shouldEqual Right(seqWidget)\n      }\n\n    \"returns a WidgetException if the service throws a exception adding the new widgets\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.addWidgets(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.addWidgets(seqWidgetData).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n  }\n\n  \"moveWidget\" should {\n\n    \"returns a the updated widget for a valid request\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(\n          Task(Either.right(Option(widget))))\n        mockPersistenceServices.updateWidgets(any) returns TaskService(\n          Task(Either.right(Seq(widgetId))))\n        val result = widgetProcess.moveWidget(widgetId, displaceX, displaceY).value.run\n        result shouldEqual Right(\n          widget.copy(\n            area = widget.area.copy(startX = startX + displaceX, startY = startY + displaceY)))\n      }\n\n    \"returns a WidgetException if the service returns a None finding the widget by Id\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(Task(Either.right(None)))\n        val result = widgetProcess.moveWidget(widgetId, displaceX, displaceY).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n\n    \"returns a WidgetException if the service throws a exception finding the widget by Id\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.moveWidget(widgetId, displaceX, displaceY).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n\n    \"returns a WidgetException if the service throws a exception updating the widget\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(\n          Task(Either.right(Option(widget))))\n        mockPersistenceServices.updateWidgets(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.moveWidget(widgetId, displaceX, displaceY).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n  }\n\n  \"resizeWidget\" should {\n\n    \"returns a the updated widget for a valid request\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(\n          Task(Either.right(Option(widget))))\n        mockPersistenceServices.updateWidgets(any) returns TaskService(\n          Task(Either.right(Seq(widgetId))))\n        val result = widgetProcess.resizeWidget(widgetId, increaseX, increaseY).value.run\n        result shouldEqual Right(\n          widget.copy(\n            area = widget.area.copy(spanX = spanX + increaseX, spanY = spanY + increaseY)))\n      }\n\n    \"returns a WidgetException if the service returns a None finding the widget by Id\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(Task(Either.right(None)))\n        val result = widgetProcess.resizeWidget(widgetId, increaseX, increaseY).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n\n    \"returns a WidgetException if the service throws a exception finding the widget by Id\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.resizeWidget(widgetId, increaseX, increaseY).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n\n    \"returns a WidgetException if the service throws a exception updating the widget\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(\n          Task(Either.right(Option(widget))))\n        mockPersistenceServices.updateWidgets(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.resizeWidget(widgetId, increaseX, increaseY).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n  }\n\n  \"updateAppWidgetId\" should {\n\n    \"returns a successful answer for a valid request\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(\n          Task(Either.right(Option(widget))))\n        mockPersistenceServices.updateWidgets(any) returns TaskService(\n          Task(Either.right(Seq(widgetId))))\n\n        val result = widgetProcess.updateAppWidgetId(widgetId, appWidgetId).value.run\n        result must beLike {\n          case Right(appWidget) =>\n            appWidget.appWidgetId shouldEqual Some(appWidgetId)\n        }\n\n      }\n\n    \"returns a WidgetException if the service throws a exception deleting the moments\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(\n          Task(Either.right(Option(widget))))\n        mockPersistenceServices.updateWidgets(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.updateAppWidgetId(widgetId, appWidgetId).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n  }\n\n  \"deleteAllWidgets\" should {\n\n    \"returns a successful answer for a valid request\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.deleteAllWidgets() returns TaskService(\n          Task(Either.right(deletedWidgets)))\n        val result = widgetProcess.deleteAllWidgets().value.run\n        result shouldEqual Right((): Unit)\n\n      }\n\n    \"returns a WidgetException if the service throws a exception deleting the moments\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.deleteAllWidgets() returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.deleteAllWidgets().value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n  }\n\n  \"deleteWidget\" should {\n\n    \"returns a successful answer for a valid request\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(\n          Task(Either.right(Option(widget))))\n        mockPersistenceServices.deleteWidget(any) returns TaskService(\n          Task(Either.right(deletedWidget)))\n        val result = widgetProcess.deleteWidget(widgetId).value.run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"returns a WidgetException if the service throws a exception finding the widget by Id\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.resizeWidget(widgetId, increaseX, increaseY).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n\n    \"returns a WidgetException if the service throws a exception deleting the moments\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.findWidgetById(any) returns TaskService(\n          Task(Either.right(Option(widget))))\n        mockPersistenceServices.deleteWidget(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.deleteWidget(widgetId).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n  }\n\n  \"deleteWidgetsByMoment\" should {\n\n    \"returns a successful answer for a valid request\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.deleteWidgetsByMoment(any) returns TaskService(\n          Task(Either.right(deletedWidgets)))\n        val result = widgetProcess.deleteWidgetsByMoment(widgetMomentId).value.run\n        result shouldEqual Right((): Unit)\n      }\n\n    \"returns a WidgetException if the service throws a exception deleting the moments\" in\n      new WidgetProcessScope {\n\n        mockPersistenceServices.deleteWidgetsByMoment(any) returns TaskService(\n          Task(Either.left(persistenceServiceException)))\n        val result = widgetProcess.deleteWidgetsByMoment(widgetMomentId).value.run\n        result must beAnInstanceOf[Left[AppWidgetException, _]]\n      }\n  }\n\n}\n"
  },
  {
    "path": "modules/repository/build.sbt",
    "content": "platformTarget in Android := \"android-24\"\n"
  },
  {
    "path": "modules/repository/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      package=\"cards.nine.repository\"\n      android:versionCode=\"1\"\n      android:versionName=\"1.0\">\n    <uses-sdk\n        android:minSdkVersion=\"16\"\n        android:targetSdkVersion=\"24\"/>\n\n    <application />\n</manifest>\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/Conversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository\n\nimport cards.nine.repository.model._\nimport cards.nine.repository.provider._\n\nobject Conversions {\n\n  def toApp(app: AppEntity): App =\n    App(\n      id = app.id,\n      data = AppData(\n        name = app.data.name,\n        packageName = app.data.packageName,\n        className = app.data.className,\n        category = app.data.category,\n        dateInstalled = app.data.dateInstalled,\n        dateUpdate = app.data.dateUpdate,\n        version = app.data.version,\n        installedFromGooglePlay = app.data.installedFromGooglePlay))\n\n  def toCard(cardEntity: CardEntity): Card =\n    Card(\n      id = cardEntity.id,\n      data = CardData(\n        position = cardEntity.data.position,\n        term = cardEntity.data.term,\n        packageName = Option[String](cardEntity.data.packageName),\n        cardType = cardEntity.data.`type`,\n        intent = cardEntity.data.intent,\n        imagePath = Option[String](cardEntity.data.imagePath),\n        notification = Option[String](cardEntity.data.notification)))\n\n  def toCollection(collectionEntity: CollectionEntity): Collection =\n    Collection(\n      id = collectionEntity.id,\n      data = CollectionData(\n        position = collectionEntity.data.position,\n        name = collectionEntity.data.name,\n        collectionType = collectionEntity.data.`type`,\n        icon = collectionEntity.data.icon,\n        themedColorIndex = collectionEntity.data.themedColorIndex,\n        appsCategory = Option[String](collectionEntity.data.appsCategory),\n        originalSharedCollectionId =\n          Option[String](collectionEntity.data.originalSharedCollectionId),\n        sharedCollectionId = Option[String](collectionEntity.data.sharedCollectionId),\n        sharedCollectionSubscribed =\n          Option[Boolean](collectionEntity.data.sharedCollectionSubscribed)))\n\n  def toDockApp(dockAppEntity: DockAppEntity): DockApp =\n    DockApp(\n      id = dockAppEntity.id,\n      data = DockAppData(\n        name = dockAppEntity.data.name,\n        dockType = dockAppEntity.data.dockType,\n        intent = dockAppEntity.data.intent,\n        imagePath = dockAppEntity.data.imagePath,\n        position = dockAppEntity.data.position))\n\n  def toMoment(momentEntity: MomentEntity): Moment =\n    Moment(\n      id = momentEntity.id,\n      data = MomentData(\n        collectionId = momentEntity.data.collectionId,\n        timeslot = momentEntity.data.timeslot,\n        wifi = momentEntity.data.wifi,\n        bluetooth = momentEntity.data.bluetooth,\n        headphone = momentEntity.data.headphone,\n        momentType = Option[String](momentEntity.data.momentType)))\n\n  def toUser(userEntity: UserEntity): User =\n    User(\n      id = userEntity.id,\n      data = UserData(\n        email = Option[String](userEntity.data.email),\n        apiKey = Option[String](userEntity.data.apiKey),\n        sessionToken = Option[String](userEntity.data.sessionToken),\n        deviceToken = Option[String](userEntity.data.deviceToken),\n        marketToken = Option[String](userEntity.data.marketToken),\n        name = Option[String](userEntity.data.name),\n        avatar = Option[String](userEntity.data.avatar),\n        cover = Option[String](userEntity.data.cover),\n        deviceName = Option[String](userEntity.data.deviceName),\n        deviceCloudId = Option[String](userEntity.data.deviceCloudId)))\n\n  def toWidget(widget: WidgetEntity): Widget =\n    Widget(\n      id = widget.id,\n      data = WidgetData(\n        momentId = widget.data.momentId,\n        packageName = widget.data.packageName,\n        className = widget.data.className,\n        appWidgetId = widget.data.appWidgetId,\n        startX = widget.data.startX,\n        startY = widget.data.startY,\n        spanX = widget.data.spanX,\n        spanY = widget.data.spanY,\n        widgetType = widget.data.widgetType,\n        label = Option[String](widget.data.label),\n        imagePath = Option[String](widget.data.imagePath),\n        intent = Option[String](widget.data.intent)))\n\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class RepositoryException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsRepositoryExceptions {\n  implicit def repositoryException = (t: Throwable) => RepositoryException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/model/Model.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.model\n\ncase class App(id: Int, data: AppData)\n\ncase class AppData(\n    name: String,\n    packageName: String,\n    className: String,\n    category: String,\n    dateInstalled: Long,\n    dateUpdate: Long,\n    version: String,\n    installedFromGooglePlay: Boolean)\n\ncase class Collection(id: Int, data: CollectionData)\n\ncase class CollectionData(\n    position: Int,\n    name: String,\n    collectionType: String,\n    icon: String,\n    themedColorIndex: Int,\n    appsCategory: Option[String] = None,\n    originalSharedCollectionId: Option[String] = None,\n    sharedCollectionId: Option[String] = None,\n    sharedCollectionSubscribed: Option[Boolean])\n\ncase class Card(id: Int, data: CardData)\n\ncase class CardsWithCollectionId(collectionId: Int, data: Seq[CardData])\n\ncase class CardData(\n    position: Int,\n    term: String,\n    packageName: Option[String],\n    cardType: String,\n    intent: String,\n    imagePath: Option[String],\n    notification: Option[String] = None)\n\ncase class DockApp(id: Int, data: DockAppData)\n\ncase class DockAppData(\n    name: String,\n    dockType: String,\n    intent: String,\n    imagePath: String,\n    position: Int)\n\ncase class Moment(id: Int, data: MomentData)\n\ncase class MomentData(\n    collectionId: Option[Int],\n    timeslot: String,\n    wifi: String,\n    bluetooth: String,\n    headphone: Boolean,\n    momentType: Option[String])\n\ncase class User(id: Int, data: UserData)\n\ncase class UserData(\n    email: Option[String],\n    apiKey: Option[String],\n    sessionToken: Option[String],\n    deviceToken: Option[String],\n    marketToken: Option[String],\n    name: Option[String],\n    avatar: Option[String],\n    cover: Option[String],\n    deviceName: Option[String],\n    deviceCloudId: Option[String])\n\ncase class Widget(id: Int, data: WidgetData)\n\ncase class WidgetData(\n    momentId: Int,\n    packageName: String,\n    className: String,\n    appWidgetId: Int,\n    startX: Int,\n    startY: Int,\n    spanX: Int,\n    spanY: Int,\n    widgetType: String,\n    label: Option[String],\n    imagePath: Option[String],\n    intent: Option[String])\n\ncase class DataCounter(term: String, count: Int)\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/provider/AppEntity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.provider\n\nimport android.database.Cursor\nimport cards.nine.repository.Conversions._\nimport cards.nine.repository.model.App\n\ncase class AppEntity(id: Int, data: AppEntityData)\n\ncase class AppEntityData(\n    name: String,\n    packageName: String,\n    className: String,\n    category: String,\n    dateInstalled: Long,\n    dateUpdate: Long,\n    version: String,\n    installedFromGooglePlay: Boolean)\n\nobject AppEntity {\n  val table                   = \"App\"\n  val name                    = \"name\"\n  val packageName             = \"packageName\"\n  val className               = \"className\"\n  val category                = \"category\"\n  val dateInstalled           = \"dateInstalled\"\n  val dateUpdate              = \"dateUpdate\"\n  val version                 = \"version\"\n  val installedFromGooglePlay = \"installedFromGooglePlay\"\n\n  val allFields = Seq[String](\n    NineCardsSqlHelper.id,\n    name,\n    packageName,\n    className,\n    category,\n    dateInstalled,\n    dateUpdate,\n    version,\n    installedFromGooglePlay)\n\n  def nameFromCursor(cursor: Cursor): String =\n    cursor.getString(cursor.getColumnIndex(name))\n\n  def categoryFromCursor(cursor: Cursor): String =\n    cursor.getString(cursor.getColumnIndex(category))\n\n  def dateInstalledFromCursor(cursor: Cursor): Long =\n    cursor.getLong(cursor.getColumnIndex(dateInstalled))\n\n  def appEntityFromCursor(cursor: Cursor): AppEntity =\n    AppEntity(\n      id = cursor.getInt(cursor.getColumnIndex(NineCardsSqlHelper.id)),\n      data = AppEntityData(\n        name = cursor.getString(cursor.getColumnIndex(name)),\n        packageName = cursor.getString(cursor.getColumnIndex(packageName)),\n        className = cursor.getString(cursor.getColumnIndex(className)),\n        category = cursor.getString(cursor.getColumnIndex(category)),\n        dateInstalled = cursor.getLong(cursor.getColumnIndex(dateInstalled)),\n        dateUpdate = cursor.getLong(cursor.getColumnIndex(dateUpdate)),\n        version = cursor.getString(cursor.getColumnIndex(version)),\n        installedFromGooglePlay = cursor\n            .getInt(cursor.getColumnIndex(installedFromGooglePlay)) > 0))\n\n  def appFromCursor(cursor: Cursor): App = toApp(appEntityFromCursor(cursor))\n\n  def createTableSQL: String =\n    s\"\"\"CREATE TABLE ${AppEntity.table}\n       |(${NineCardsSqlHelper.id} INTEGER PRIMARY KEY AUTOINCREMENT,\n       |${AppEntity.name} TEXT not null,\n       |${AppEntity.packageName} TEXT not null,\n       |${AppEntity.className} TEXT not null,\n       |${AppEntity.category} TEXT not null,\n       |${AppEntity.dateInstalled} INTEGER,\n       |${AppEntity.dateUpdate} INTEGER,\n       |${AppEntity.version} TEXT not null,\n       |${AppEntity.installedFromGooglePlay} INTEGER )\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/provider/CardEntity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.provider\n\nimport android.database.Cursor\nimport cards.nine.repository.Conversions._\nimport cards.nine.repository.model.Card\n\ncase class CardEntity(id: Int, data: CardEntityData)\n\ncase class CardEntityData(\n    position: Int,\n    collectionId: Int,\n    term: String,\n    packageName: String,\n    `type`: String,\n    intent: String,\n    imagePath: String,\n    notification: String)\n\nobject CardEntity {\n  val table        = \"Card\"\n  val position     = \"position\"\n  val collectionId = \"collection_id\"\n  val term         = \"term\"\n  val packageName  = \"packageName\"\n  val cardType     = \"type\"\n  val intent       = \"intent\"\n  val imagePath    = \"imagePath\"\n  val notification = \"notification\"\n\n  val allFields = Seq[String](\n    NineCardsSqlHelper.id,\n    position,\n    collectionId,\n    term,\n    packageName,\n    cardType,\n    intent,\n    imagePath,\n    notification)\n\n  def cardEntityFromCursor(cursor: Cursor): CardEntity =\n    CardEntity(\n      id = cursor.getInt(cursor.getColumnIndex(NineCardsSqlHelper.id)),\n      data = CardEntityData(\n        position = cursor.getInt(cursor.getColumnIndex(position)),\n        collectionId = cursor.getInt(cursor.getColumnIndex(collectionId)),\n        term = cursor.getString(cursor.getColumnIndex(term)),\n        packageName = cursor.getString(cursor.getColumnIndex(packageName)),\n        `type` = cursor.getString(cursor.getColumnIndex(cardType)),\n        intent = cursor.getString(cursor.getColumnIndex(intent)),\n        imagePath = cursor.getString(cursor.getColumnIndex(imagePath)),\n        notification = cursor.getString(cursor.getColumnIndex(notification))))\n\n  def cardFromCursor(cursor: Cursor): Card = toCard(cardEntityFromCursor(cursor))\n\n  def createTableSQL: String =\n    s\"\"\"CREATE TABLE ${CardEntity.table}\n       |(${NineCardsSqlHelper.id} INTEGER PRIMARY KEY AUTOINCREMENT,\n       |${CardEntity.position} INTEGER not null,\n       |${CardEntity.collectionId} INTEGER not null,\n       |${CardEntity.term} TEXT not null,\n       |${CardEntity.packageName} TEXT,\n       |${CardEntity.cardType} TEXT not null,\n       |${CardEntity.intent} TEXT,\n       |${CardEntity.imagePath} TEXT,\n       |${CardEntity.notification} TEXT)\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/provider/CollectionEntity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.provider\n\nimport android.database.Cursor\nimport cards.nine.repository.Conversions._\nimport cards.nine.repository.model.Collection\n\ncase class CollectionEntity(id: Int, data: CollectionEntityData)\n\ncase class CollectionEntityData(\n    position: Int,\n    name: String,\n    `type`: String,\n    icon: String,\n    themedColorIndex: Int,\n    appsCategory: String,\n    originalSharedCollectionId: String,\n    sharedCollectionId: String,\n    sharedCollectionSubscribed: Boolean)\n\nobject CollectionEntity {\n  val table                      = \"Collection\"\n  val position                   = \"position\"\n  val name                       = \"name\"\n  val collectionType             = \"type\"\n  val icon                       = \"icon\"\n  val themedColorIndex           = \"themedColorIndex\"\n  val appsCategory               = \"appsCategory\"\n  val originalSharedCollectionId = \"originalSharedCollectionId\"\n  val sharedCollectionId         = \"sharedCollectionId\"\n  val sharedCollectionSubscribed = \"sharedCollectionSubscribed\"\n\n  val allFields = Seq[String](\n    NineCardsSqlHelper.id,\n    position,\n    name,\n    collectionType,\n    icon,\n    themedColorIndex,\n    appsCategory,\n    originalSharedCollectionId,\n    sharedCollectionId,\n    sharedCollectionSubscribed)\n\n  def collectionEntityFromCursor(cursor: Cursor): CollectionEntity =\n    CollectionEntity(\n      id = cursor.getInt(cursor.getColumnIndex(NineCardsSqlHelper.id)),\n      data = CollectionEntityData(\n        position = cursor.getInt(cursor.getColumnIndex(position)),\n        name = cursor.getString(cursor.getColumnIndex(name)),\n        `type` = cursor.getString(cursor.getColumnIndex(collectionType)),\n        icon = cursor.getString(cursor.getColumnIndex(icon)),\n        themedColorIndex = cursor.getInt(cursor.getColumnIndex(themedColorIndex)),\n        appsCategory = cursor.getString(cursor.getColumnIndex(appsCategory)),\n        originalSharedCollectionId =\n          cursor.getString(cursor.getColumnIndex(originalSharedCollectionId)),\n        sharedCollectionId = cursor.getString(cursor.getColumnIndex(sharedCollectionId)),\n        sharedCollectionSubscribed = cursor.getInt(\n            cursor.getColumnIndex(sharedCollectionSubscribed)) > 0))\n\n  def collectionFromCursor(cursor: Cursor): Collection =\n    toCollection(collectionEntityFromCursor(cursor))\n\n  def createTableSQL: String =\n    s\"\"\"CREATE TABLE ${CollectionEntity.table}\n       |(${NineCardsSqlHelper.id} INTEGER PRIMARY KEY AUTOINCREMENT,\n       |${CollectionEntity.position} INTEGER not null,\n       |${CollectionEntity.name} TEXT not null,\n       |${CollectionEntity.collectionType} TEXT not null,\n       |${CollectionEntity.icon} TEXT not null,\n       |${CollectionEntity.themedColorIndex} INTEGER not null,\n       |${CollectionEntity.appsCategory} TEXT,\n       |${CollectionEntity.originalSharedCollectionId} TEXT,\n       |${CollectionEntity.sharedCollectionId} TEXT,\n       |${CollectionEntity.sharedCollectionSubscribed} INTEGER)\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/provider/DockAppEntity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.provider\n\nimport android.database.Cursor\nimport cards.nine.repository.model.DockApp\nimport cards.nine.repository.Conversions._\n\ncase class DockAppEntity(id: Int, data: DockAppEntityData)\n\ncase class DockAppEntityData(\n    name: String,\n    dockType: String,\n    intent: String,\n    imagePath: String,\n    position: Int)\n\nobject DockAppEntity {\n  val table     = \"DockApp\"\n  val name      = \"name\"\n  val dockType  = \"dockType\"\n  val intent    = \"intent\"\n  val imagePath = \"imagePath\"\n  val position  = \"position\"\n\n  val allFields = Seq[String](NineCardsSqlHelper.id, name, dockType, intent, imagePath, position)\n\n  def dockAppEntityFromCursor(cursor: Cursor): DockAppEntity =\n    DockAppEntity(\n      id = cursor.getInt(cursor.getColumnIndex(NineCardsSqlHelper.id)),\n      data = DockAppEntityData(\n        name = cursor.getString(cursor.getColumnIndex(name)),\n        dockType = cursor.getString(cursor.getColumnIndex(dockType)),\n        intent = cursor.getString(cursor.getColumnIndex(intent)),\n        imagePath = cursor.getString(cursor.getColumnIndex(imagePath)),\n        position = cursor.getInt(cursor.getColumnIndex(position))))\n\n  def dockAppFromCursor(cursor: Cursor): DockApp = toDockApp(dockAppEntityFromCursor(cursor))\n\n  def createTableSQL: String =\n    s\"\"\"CREATE TABLE ${DockAppEntity.table}\n        |(${NineCardsSqlHelper.id} INTEGER PRIMARY KEY AUTOINCREMENT,\n        |${DockAppEntity.name} TEXT not null,\n        |${DockAppEntity.dockType} TEXT not null,\n        |${DockAppEntity.intent} TEXT not null,\n        |${DockAppEntity.imagePath} TEXT not null,\n        |${DockAppEntity.position} INTEGER not null)\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/provider/MomentEntity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.provider\n\nimport android.database.Cursor\nimport cards.nine.repository.model.Moment\nimport cards.nine.repository.Conversions._\n\ncase class MomentEntity(id: Int, data: MomentEntityData)\n\ncase class MomentEntityData(\n    collectionId: Option[Int],\n    timeslot: String,\n    wifi: String,\n    bluetooth: String,\n    headphone: Boolean,\n    momentType: String)\n\nobject MomentEntity {\n  val table        = \"Moment\"\n  val collectionId = \"collectionId\"\n  val timeslot     = \"timeslot\"\n  val wifi         = \"wifi\"\n  val bluetooth    = \"bluetooth\"\n  val headphone    = \"headphone\"\n  val momentType   = \"momentType\"\n\n  val allFields =\n    Seq[String](\n      NineCardsSqlHelper.id,\n      collectionId,\n      timeslot,\n      wifi,\n      bluetooth,\n      headphone,\n      momentType)\n\n  def momentEntityFromCursor(cursor: Cursor): MomentEntity = {\n    val collectionIdColumn = cursor.getColumnIndex(collectionId)\n    val bluetoothColumn    = cursor.getColumnIndex(bluetooth)\n    MomentEntity(\n      id = cursor.getInt(cursor.getColumnIndex(NineCardsSqlHelper.id)),\n      data = MomentEntityData(\n        collectionId =\n          if (cursor.isNull(collectionIdColumn)) None\n          else Option(cursor.getInt(collectionIdColumn)),\n        timeslot = cursor.getString(cursor.getColumnIndex(timeslot)),\n        wifi = cursor.getString(cursor.getColumnIndex(wifi)),\n        bluetooth = if (cursor.isNull(bluetoothColumn)) \"\" else cursor.getString(bluetoothColumn),\n        headphone = cursor.getInt(cursor.getColumnIndex(headphone)) > 0,\n        momentType = cursor.getString(cursor.getColumnIndex(momentType))))\n  }\n\n  def momentFromCursor(cursor: Cursor): Moment = toMoment(momentEntityFromCursor(cursor))\n\n  def createTableSQL: String =\n    s\"\"\"CREATE TABLE ${MomentEntity.table}\n        |(${NineCardsSqlHelper.id} INTEGER PRIMARY KEY AUTOINCREMENT,\n        |${MomentEntity.collectionId} INTEGER,\n        |${MomentEntity.timeslot} TEXT not null,\n        |${MomentEntity.wifi} TEXT not null,\n        |${MomentEntity.headphone} INTEGER not null,\n        |${MomentEntity.momentType} TEXT,\n        |${MomentEntity.bluetooth} TEXT)\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/provider/NineCardsContentProvider.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.provider\n\nimport android.content.{ContentProvider, ContentUris, ContentValues, UriMatcher}\nimport android.database.Cursor\nimport android.database.sqlite.{SQLiteDatabase, SQLiteQueryBuilder}\nimport android.net.Uri\nimport cards.nine.repository.provider.NineCardsContentProvider._\nimport cards.nine.repository.provider.NineCardsUri._\nimport cards.nine.commons.javaNull\n\nclass NineCardsContentProvider extends ContentProvider {\n\n  lazy val nineCardsSqlHelper = new NineCardsSqlHelper(getContext)\n\n  lazy val database: Option[SQLiteDatabase] =\n    Option[SQLiteDatabase](nineCardsSqlHelper.getWritableDatabase)\n\n  private[this] def getUriInfo(uri: Uri): (String, MimeType) =\n    uriMatcher.`match`(uri) match {\n      case `codeCardAllItems`         => (CardEntity.table, MimeTypeAllItems)\n      case `codeCardSingleItem`       => (CardEntity.table, MimeTypeSingleItem)\n      case `codeCollectionAllItems`   => (CollectionEntity.table, MimeTypeAllItems)\n      case `codeCollectionSingleItem` => (CollectionEntity.table, MimeTypeSingleItem)\n      case `codeAppAllItems`          => (AppEntity.table, MimeTypeAllItems)\n      case `codeAppSingleItem`        => (AppEntity.table, MimeTypeSingleItem)\n      case `codeDockAppAllItems`      => (DockAppEntity.table, MimeTypeAllItems)\n      case `codeDockAppSingleItem`    => (DockAppEntity.table, MimeTypeSingleItem)\n      case `codeMomentAllItems`       => (MomentEntity.table, MimeTypeAllItems)\n      case `codeMomentSingleItem`     => (MomentEntity.table, MimeTypeSingleItem)\n      case `codeUserAllItems`         => (UserEntity.table, MimeTypeAllItems)\n      case `codeUserSingleItem`       => (UserEntity.table, MimeTypeSingleItem)\n      case `codeWidgetAllItems`       => (WidgetEntity.table, MimeTypeAllItems)\n      case `codeWidgetSingleItem`     => (WidgetEntity.table, MimeTypeSingleItem)\n\n      case _ => throw new IllegalArgumentException(invalidUri + uri)\n    }\n\n  override def onCreate(): Boolean =\n    database match {\n      case Some(databaseObject) if databaseObject.isOpen => true\n      case _                                             => false\n    }\n\n  override def onLowMemory() = {\n    super.onLowMemory()\n    nineCardsSqlHelper.close()\n  }\n\n  override def getType(uri: Uri): String =\n    getUriInfo(uri) match {\n      case (_, MimeTypeAllItems)   => mimeTypeAllItemsValue\n      case (_, MimeTypeSingleItem) => mimeTypeSingleItemValue\n    }\n\n  override def update(\n      uri: Uri,\n      values: ContentValues,\n      selection: String,\n      selectionArgs: Array[String]): Int =\n    getUriInfo(uri) match {\n      case (tableName, MimeTypeSingleItem) =>\n        getOrOpenDatabase.update(\n          tableName,\n          values,\n          s\"${NineCardsSqlHelper.id} = ?\",\n          Seq(uri.getPathSegments.get(1)).toArray)\n      case (tableName, MimeTypeAllItems) =>\n        getOrOpenDatabase.update(tableName, values, selection, selectionArgs)\n    }\n\n  override def insert(uri: Uri, values: ContentValues): Uri =\n    getUriInfo(uri) match {\n      case (tableName, MimeTypeAllItems) =>\n        ContentUris.withAppendedId(\n          uri,\n          getOrOpenDatabase.insert(tableName, NineCardsSqlHelper.databaseName, values))\n      case _ =>\n        throw new IllegalArgumentException(invalidUri + uri)\n    }\n\n  override def delete(uri: Uri, selection: String, selectionArgs: Array[String]): Int =\n    getUriInfo(uri) match {\n      case (tableName, MimeTypeSingleItem) =>\n        getOrOpenDatabase.delete(\n          tableName,\n          s\"${NineCardsSqlHelper.id} = ?\",\n          Seq(uri.getPathSegments.get(1)).toArray)\n      case (tableName, MimeTypeAllItems) =>\n        getOrOpenDatabase.delete(tableName, selection, selectionArgs)\n    }\n\n  override def query(\n      uri: Uri,\n      projection: Array[String],\n      selection: String,\n      selectionArgs: Array[String],\n      sortOrder: String): Cursor =\n    getUriInfo(uri) match {\n      case (tableName, MimeTypeSingleItem) =>\n        val queryBuilder = new SQLiteQueryBuilder()\n        queryBuilder.setTables(tableName)\n        queryBuilder.query(\n          getOrOpenDatabase,\n          projection,\n          s\"${NineCardsSqlHelper.id} = ?\",\n          Seq(uri.getPathSegments.get(1)).toArray,\n          javaNull,\n          javaNull,\n          sortOrder)\n      case (tableName, MimeTypeAllItems) =>\n        val queryBuilder = new SQLiteQueryBuilder()\n        queryBuilder.setTables(tableName)\n        queryBuilder.query(\n          getOrOpenDatabase,\n          projection,\n          selection,\n          selectionArgs,\n          javaNull,\n          javaNull,\n          sortOrder)\n    }\n\n  private[this] def getOrOpenDatabase = database match {\n    case Some(databaseObject) if databaseObject.isOpen => databaseObject\n    case _                                             => nineCardsSqlHelper.getWritableDatabase\n  }\n}\n\nobject NineCardsContentProvider {\n\n  val invalidUri               = \"Invalid uri: \"\n  val codeAppAllItems          = 1\n  val codeAppSingleItem        = 2\n  val codeCardAllItems         = 3\n  val codeCardSingleItem       = 4\n  val codeCollectionAllItems   = 5\n  val codeCollectionSingleItem = 6\n  val codeDockAppAllItems      = 7\n  val codeDockAppSingleItem    = 8\n  val codeMomentAllItems       = 9\n  val codeMomentSingleItem     = 10\n  val codeUserAllItems         = 11\n  val codeUserSingleItem       = 12\n  val codeWidgetAllItems       = 13\n  val codeWidgetSingleItem     = 14\n  val mimeTypeAllItemsValue    = \"vnd.android.cursor.dir/vnd.cards.nine\"\n  val mimeTypeSingleItemValue  = \"vnd.android.cursor.item/vnd.cards.nine\"\n\n  val uriMatcher = new UriMatcher(UriMatcher.NO_MATCH)\n  uriMatcher.addURI(authorityPart, AppEntity.table, codeAppAllItems)\n  uriMatcher.addURI(authorityPart, s\"${AppEntity.table}/#\", codeAppSingleItem)\n  uriMatcher.addURI(authorityPart, CardEntity.table, codeCardAllItems)\n  uriMatcher.addURI(authorityPart, s\"${CardEntity.table}/#\", codeCardSingleItem)\n  uriMatcher.addURI(authorityPart, CollectionEntity.table, codeCollectionAllItems)\n  uriMatcher.addURI(authorityPart, s\"${CollectionEntity.table}/#\", codeCollectionSingleItem)\n  uriMatcher.addURI(authorityPart, DockAppEntity.table, codeDockAppAllItems)\n  uriMatcher.addURI(authorityPart, s\"${DockAppEntity.table}/#\", codeDockAppSingleItem)\n  uriMatcher.addURI(authorityPart, MomentEntity.table, codeMomentAllItems)\n  uriMatcher.addURI(authorityPart, s\"${MomentEntity.table}/#\", codeMomentSingleItem)\n  uriMatcher.addURI(authorityPart, UserEntity.table, codeUserAllItems)\n  uriMatcher.addURI(authorityPart, s\"${UserEntity.table}/#\", codeUserSingleItem)\n  uriMatcher.addURI(authorityPart, WidgetEntity.table, codeWidgetAllItems)\n  uriMatcher.addURI(authorityPart, s\"${WidgetEntity.table}/#\", codeWidgetSingleItem)\n}\n\nsealed trait MimeType\n\nobject MimeTypeAllItems extends MimeType\n\nobject MimeTypeSingleItem extends MimeType\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/provider/NineCardsSqlHelper.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.provider\n\nimport android.content.Context\nimport android.database.sqlite.{SQLiteDatabase, SQLiteOpenHelper}\nimport cards.nine.commons.javaNull\n\nclass NineCardsSqlHelper(context: Context)\n    extends SQLiteOpenHelper(\n      context,\n      NineCardsSqlHelper.databaseName,\n      javaNull,\n      NineCardsSqlHelper.databaseVersion) {\n\n  override def onCreate(db: SQLiteDatabase): Unit = {\n    db.execSQL(AppEntity.createTableSQL)\n    db.execSQL(CollectionEntity.createTableSQL)\n    db.execSQL(CardEntity.createTableSQL)\n    db.execSQL(DockAppEntity.createTableSQL)\n    db.execSQL(MomentEntity.createTableSQL)\n    db.execSQL(UserEntity.createTableSQL)\n    db.execSQL(WidgetEntity.createTableSQL)\n  }\n\n  override def onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int): Unit =\n    (oldVersion + 1 to newVersion) foreach {\n      case 2 =>\n        db.execSQL(s\"ALTER TABLE ${MomentEntity.table} ADD COLUMN ${MomentEntity.bluetooth} TEXT\")\n    }\n\n}\n\nobject NineCardsSqlHelper {\n  val id              = \"_id\"\n  val databaseName    = \"nine-cards.db\"\n  val databaseVersion = 2\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/provider/NineCardsUri.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.provider\n\nobject NineCardsUri {\n\n  val authorityPart = \"com.fortysevendeg.ninecardslauncher\"\n\n  val contentPrefix = \"content://\"\n\n  val baseUriString = s\"$contentPrefix$authorityPart\"\n\n  val appUriString = s\"$baseUriString/${AppEntity.table}\"\n\n  val cardUriString = s\"$baseUriString/${CardEntity.table}\"\n\n  val collectionUriString = s\"$baseUriString/${CollectionEntity.table}\"\n\n  val dockAppUriString = s\"$baseUriString/${DockAppEntity.table}\"\n\n  val momentUriString = s\"$baseUriString/${MomentEntity.table}\"\n\n  val userUriString = s\"$baseUriString/${UserEntity.table}\"\n\n  val widgetUriString = s\"$baseUriString/${WidgetEntity.table}\"\n\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/provider/UserEntity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.provider\n\nimport android.database.Cursor\nimport cards.nine.repository.model.User\nimport cards.nine.repository.Conversions._\n\ncase class UserEntity(id: Int, data: UserEntityData)\n\ncase class UserEntityData(\n    email: String,\n    apiKey: String,\n    sessionToken: String,\n    deviceToken: String,\n    marketToken: String,\n    name: String,\n    avatar: String,\n    cover: String,\n    deviceName: String,\n    deviceCloudId: String)\n\nobject UserEntity {\n  val table         = \"User\"\n  val email         = \"email\"\n  val apiKey        = \"apiKey\"\n  val sessionToken  = \"sessionToken\"\n  val deviceToken   = \"deviceToken\"\n  val marketToken   = \"marketToken\"\n  val name          = \"name\"\n  val avatar        = \"avatar\"\n  val cover         = \"cover\"\n  val deviceName    = \"deviceName\"\n  val deviceCloudId = \"deviceCloudId\"\n\n  val allFields = Seq[String](\n    NineCardsSqlHelper.id,\n    email,\n    apiKey,\n    sessionToken,\n    deviceToken,\n    marketToken,\n    name,\n    avatar,\n    cover,\n    deviceName,\n    deviceCloudId)\n\n  def userEntityFromCursor(cursor: Cursor): UserEntity =\n    UserEntity(\n      id = cursor.getInt(cursor.getColumnIndex(NineCardsSqlHelper.id)),\n      data = UserEntityData(\n        email = cursor.getString(cursor.getColumnIndex(email)),\n        apiKey = cursor.getString(cursor.getColumnIndex(apiKey)),\n        sessionToken = cursor.getString(cursor.getColumnIndex(sessionToken)),\n        deviceToken = cursor.getString(cursor.getColumnIndex(deviceToken)),\n        marketToken = cursor.getString(cursor.getColumnIndex(marketToken)),\n        name = cursor.getString(cursor.getColumnIndex(name)),\n        avatar = cursor.getString(cursor.getColumnIndex(avatar)),\n        cover = cursor.getString(cursor.getColumnIndex(cover)),\n        deviceName = cursor.getString(cursor.getColumnIndex(deviceName)),\n        deviceCloudId = cursor.getString(cursor.getColumnIndex(deviceCloudId))))\n\n  def userFromCursor(cursor: Cursor): User = toUser(userEntityFromCursor(cursor))\n\n  def createTableSQL: String =\n    s\"\"\"CREATE TABLE ${UserEntity.table}\n        |(${NineCardsSqlHelper.id} INTEGER PRIMARY KEY AUTOINCREMENT,\n        |${UserEntity.email} TEXT,\n        |${UserEntity.apiKey} TEXT,\n        |${UserEntity.sessionToken} TEXT,\n        |${UserEntity.deviceToken} TEXT,\n        |${UserEntity.marketToken} TEXT,\n        |${UserEntity.name} TEXT,\n        |${UserEntity.avatar} TEXT,\n        |${UserEntity.cover} TEXT,\n        |${UserEntity.deviceName} TEXT,\n        |${UserEntity.deviceCloudId} TEXT)\"\"\".stripMargin\n\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/provider/WidgetEntity.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.provider\n\nimport android.database.Cursor\nimport cards.nine.repository.Conversions._\nimport cards.nine.repository.model.Widget\n\ncase class WidgetEntity(id: Int, data: WidgetEntityData)\n\ncase class WidgetEntityData(\n    momentId: Int,\n    packageName: String,\n    className: String,\n    appWidgetId: Int,\n    startX: Int,\n    startY: Int,\n    spanX: Int,\n    spanY: Int,\n    widgetType: String,\n    label: String,\n    imagePath: String,\n    intent: String)\n\nobject WidgetEntity {\n  val table       = \"Widget\"\n  val momentId    = \"momentId\"\n  val packageName = \"packageName\"\n  val className   = \"className\"\n  val appWidgetId = \"appWidgetId\"\n  val startX      = \"startX\"\n  val startY      = \"startY\"\n  val spanX       = \"spanX\"\n  val spanY       = \"spanY\"\n  val widgetType  = \"widgetType\"\n  val label       = \"label\"\n  val imagePath   = \"imagePath\"\n  val intent      = \"intent\"\n\n  val allFields = Seq[String](\n    NineCardsSqlHelper.id,\n    momentId,\n    packageName,\n    className,\n    appWidgetId,\n    startX,\n    startY,\n    spanX,\n    spanY,\n    widgetType,\n    label,\n    imagePath,\n    intent)\n\n  def widgetEntityFromCursor(cursor: Cursor): WidgetEntity =\n    WidgetEntity(\n      id = cursor.getInt(cursor.getColumnIndex(NineCardsSqlHelper.id)),\n      data = WidgetEntityData(\n        momentId = cursor.getInt(cursor.getColumnIndex(momentId)),\n        packageName = cursor.getString(cursor.getColumnIndex(packageName)),\n        className = cursor.getString(cursor.getColumnIndex(className)),\n        appWidgetId = cursor.getInt(cursor.getColumnIndex(appWidgetId)),\n        startX = cursor.getInt(cursor.getColumnIndex(startX)),\n        startY = cursor.getInt(cursor.getColumnIndex(startY)),\n        spanX = cursor.getInt(cursor.getColumnIndex(spanX)),\n        spanY = cursor.getInt(cursor.getColumnIndex(spanY)),\n        widgetType = cursor.getString(cursor.getColumnIndex(widgetType)),\n        label = cursor.getString(cursor.getColumnIndex(label)),\n        imagePath = cursor.getString(cursor.getColumnIndex(imagePath)),\n        intent = cursor.getString(cursor.getColumnIndex(intent))))\n\n  def widgetFromCursor(cursor: Cursor): Widget = toWidget(widgetEntityFromCursor(cursor))\n\n  def createTableSQL: String =\n    s\"\"\"CREATE TABLE ${WidgetEntity.table}\n        |(${NineCardsSqlHelper.id} INTEGER PRIMARY KEY AUTOINCREMENT,\n        |${WidgetEntity.momentId} INTEGER not null,\n        |${WidgetEntity.packageName} TEXT not null,\n        |${WidgetEntity.className} TEXT not null,\n        |${WidgetEntity.appWidgetId} INTEGER not null,\n        |${WidgetEntity.startX} INTEGER not null,\n        |${WidgetEntity.startY} INTEGER not null,\n        |${WidgetEntity.spanX} INTEGER not null,\n        |${WidgetEntity.spanY} INTEGER not null,\n        |${WidgetEntity.widgetType} TEXT not null,\n        |${WidgetEntity.label} TEXT,\n        |${WidgetEntity.imagePath} TEXT,\n        |${WidgetEntity.intent} TEXT)\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/repositories/AppRepository.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.repositories\n\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.NotificationUri._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapper, UriCreator}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.IterableCursor\nimport cards.nine.models.IterableCursor._\nimport cards.nine.repository.Conversions.toApp\nimport cards.nine.repository.model.{App, AppData, DataCounter}\nimport cards.nine.repository.provider.AppEntity._\nimport cards.nine.repository.provider.NineCardsUri._\nimport cards.nine.repository.provider.{AppEntity, NineCardsUri}\nimport cards.nine.repository.{ImplicitsRepositoryExceptions, RepositoryException}\nimport org.joda.time.DateTime\n\nclass AppRepository(contentResolverWrapper: ContentResolverWrapper, uriCreator: UriCreator)\n    extends ImplicitsRepositoryExceptions {\n\n  val appUri = uriCreator.parse(appUriString)\n\n  val appNotificationUri = uriCreator.parse(s\"$baseUriNotificationString/$appUriPath\")\n\n  val abc = \"ABCDEFGHIJKLMNÑOPQRSTUVWXYZ\"\n\n  val wildcard = \"#\"\n\n  val game = \"GAME\"\n\n  def addApp(data: AppData): TaskService[App] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(data)\n\n        val id = contentResolverWrapper\n          .insert(uri = appUri, values = values, notificationUris = Seq(appNotificationUri))\n\n        App(id = id, data = data)\n      }\n    }\n\n  def addApps(datas: Seq[AppData]): TaskService[Unit] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = datas map createMapValues\n\n        contentResolverWrapper.inserts(\n          authority = NineCardsUri.authorityPart,\n          uri = appUri,\n          allValues = values,\n          notificationUris = Seq(appNotificationUri))\n      }\n    }\n\n  def deleteApps(where: String = \"\"): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .delete(uri = appUri, where = where, notificationUris = Seq(appNotificationUri))\n      }\n    }\n\n  def deleteApp(app: App): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .deleteById(uri = appUri, id = app.id, notificationUris = Seq(appNotificationUri))\n      }\n    }\n\n  def deleteAppByPackage(packageName: String): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.delete(\n          uri = appUri,\n          where = s\"${AppEntity.packageName} = ?\",\n          whereParams = Seq(packageName),\n          notificationUris = Seq(appNotificationUri))\n      }\n    }\n\n  def fetchApps(orderBy: String = \"\"): TaskService[Seq[App]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.fetchAll(uri = appUri, projection = allFields, orderBy = orderBy)(\n          getListFromCursor(appEntityFromCursor)) map toApp\n      }\n    }\n\n  def fetchIterableApps(\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\"): TaskService[IterableCursor[App]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .getCursor(\n            uri = appUri,\n            projection = allFields,\n            where = where,\n            whereParams = whereParams,\n            orderBy = orderBy)\n          .toIterator(appFromCursor)\n      }\n    }\n\n  def fetchAlphabeticalAppsCounter: TaskService[Seq[DataCounter]] =\n    toDataCounter(\n      fetchData = getNamesAlphabetically,\n      normalize = (name: String) =>\n        name.substring(0, 1).toUpperCase match {\n          case t if abc.contains(t) => t\n          case _                    => wildcard\n      })\n\n  def fetchCategorizedAppsCounter: TaskService[Seq[DataCounter]] =\n    toDataCounter(fetchData = getCategoriesAlphabetically, normalize = {\n      case t if t.startsWith(game) => game\n      case t                       => t\n    })\n\n  def fetchInstallationDateAppsCounter: TaskService[Seq[DataCounter]] =\n    toInstallationDateDataCounter(fetchData = getInstallationDate)\n\n  def findAppById(id: Int): TaskService[Option[App]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.findById(uri = appUri, id = id, projection = allFields)(\n          getEntityFromCursor(appEntityFromCursor)) map toApp\n      }\n    }\n\n  def fetchAppByPackage(packageName: String): TaskService[Option[App]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.fetch(\n          uri = appUri,\n          projection = allFields,\n          where = s\"${AppEntity.packageName} = ?\",\n          whereParams = Seq(packageName))(getEntityFromCursor(appEntityFromCursor)) map toApp\n      }\n    }\n\n  def fetchAppByPackages(packageNames: Seq[String]): TaskService[Seq[App]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .fetchAll(\n            uri = appUri,\n            projection = allFields,\n            where =\n              s\"${AppEntity.packageName} IN (${packageNames.map(p => s\"'$p'\").mkString(\",\")})\")(\n            getListFromCursor(appEntityFromCursor)) map toApp\n      }\n    }\n\n  def fetchAppsByCategory(category: String, orderBy: String = \"\"): TaskService[Seq[App]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val (where, param) = whereCategory(category)\n        contentResolverWrapper.fetchAll(\n          uri = appUri,\n          projection = allFields,\n          where = where,\n          whereParams = Seq(param),\n          orderBy = orderBy)(getListFromCursor(appEntityFromCursor)) map toApp\n      }\n    }\n\n  def fetchIterableAppsByCategory(\n      category: String,\n      orderBy: String = \"\"): TaskService[IterableCursor[App]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val (where, param) = whereCategory(category)\n        contentResolverWrapper\n          .getCursor(\n            uri = appUri,\n            projection = allFields,\n            where = where,\n            whereParams = Seq(param),\n            orderBy = orderBy)\n          .toIterator(appFromCursor)\n      }\n    }\n\n  def updateApp(app: App): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(app.data)\n\n        contentResolverWrapper.updateById(\n          uri = appUri,\n          id = app.id,\n          values = values,\n          notificationUris = Seq(appNotificationUri)\n        )\n      }\n    }\n\n  protected def getNamesAlphabetically: Seq[String] =\n    getListFromCursor(nameFromCursor)(\n      contentResolverWrapper\n        .getCursor(uri = appUri, projection = Seq(name), orderBy = s\"$name COLLATE NOCASE ASC\"))\n\n  protected def getCategoriesAlphabetically: Seq[String] =\n    getListFromCursor(categoryFromCursor)(\n      contentResolverWrapper.getCursor(\n        uri = appUri,\n        projection = Seq(category),\n        orderBy = s\"$category COLLATE NOCASE ASC\"))\n\n  protected def getInstallationDate: Seq[Long] =\n    getListFromCursor(dateInstalledFromCursor)(contentResolverWrapper\n      .getCursor(uri = appUri, projection = Seq(dateInstalled), orderBy = s\"$dateInstalled DESC\"))\n\n  private[this] def whereCategory(category: String): (String, String) = category match {\n    case t if t.startsWith(game) =>\n      (s\"${AppEntity.category} LIKE ?\", s\"$category%\")\n    case _ =>\n      (s\"${AppEntity.category} = ?\", category)\n  }\n\n  private[this] def toDataCounter(\n      fetchData: => Seq[String],\n      normalize: (String) => String = (term) => term): TaskService[Seq[DataCounter]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val data = fetchData\n        data.foldLeft(Seq.empty[DataCounter]) { (acc, name) =>\n          val term = normalize(name)\n          val lastWithSameTerm = acc.lastOption flatMap {\n            case last if last.term == term => Some(last)\n            case _                         => None\n          }\n          lastWithSameTerm map { c =>\n            acc.dropRight(1) :+ c.copy(count = c.count + 1)\n          } getOrElse acc :+ DataCounter(term, 1)\n        }\n      }\n    }\n\n  private[this] def toInstallationDateDataCounter(\n      fetchData: => Seq[Long]): TaskService[Seq[DataCounter]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val now            = new DateTime()\n        val moreOfTwoMoths = \"moreOfTwoMoths\"\n        val dates = Seq(\n          InstallationDateInterval(\"oneWeek\", now.minusWeeks(1)),\n          InstallationDateInterval(\"twoWeeks\", now.minusWeeks(2)),\n          InstallationDateInterval(\"oneMonth\", now.minusMonths(1)),\n          InstallationDateInterval(\"twoMonths\", now.minusMonths(2)),\n          InstallationDateInterval(\"fourMonths\", now.minusMonths(4)),\n          InstallationDateInterval(\"sixMonths\", now.minusMonths(6)))\n        val data = fetchData\n        data.foldLeft(Seq.empty[DataCounter]) { (acc, date) =>\n          val installationDate = new DateTime(date)\n          val term             = termInterval(installationDate, dates) map (_.term) getOrElse moreOfTwoMoths\n          val lastWithSameTerm = acc.lastOption flatMap {\n            case last if last.term == term => Some(last)\n            case _                         => None\n          }\n          lastWithSameTerm map { c =>\n            acc.dropRight(1) :+ c.copy(count = c.count + 1)\n          } getOrElse acc :+ DataCounter(term, 1)\n        }\n      }\n    }\n\n  private[this] def termInterval(\n      installationDate: DateTime,\n      intervals: Seq[InstallationDateInterval]): Option[InstallationDateInterval] =\n    intervals find { interval =>\n      installationDate.isAfter(interval.date)\n    }\n\n  private[this] def createMapValues(data: AppData) =\n    Map[String, Any](\n      name                    -> data.name,\n      packageName             -> data.packageName,\n      className               -> data.className,\n      category                -> data.category,\n      dateInstalled           -> data.dateInstalled,\n      dateUpdate              -> data.dateUpdate,\n      version                 -> data.version,\n      installedFromGooglePlay -> data.installedFromGooglePlay)\n\n  case class InstallationDateInterval(term: String, date: DateTime)\n\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/repositories/CardRepository.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.repositories\n\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.NotificationUri._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapper, UriCreator}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.IterableCursor\nimport cards.nine.models.IterableCursor._\nimport cards.nine.repository.Conversions.toCard\nimport cards.nine.repository.model.{Card, CardData, CardsWithCollectionId}\nimport cards.nine.repository.provider.CardEntity._\nimport cards.nine.repository.provider.NineCardsUri._\nimport cards.nine.repository.provider.{CardEntity, NineCardsUri}\nimport cards.nine.repository.repositories.RepositoryUtils._\nimport cards.nine.repository.{ImplicitsRepositoryExceptions, RepositoryException}\n\nimport scala.language.postfixOps\n\nclass CardRepository(contentResolverWrapper: ContentResolverWrapper, uriCreator: UriCreator)\n    extends ImplicitsRepositoryExceptions {\n\n  val cardUri = uriCreator.parse(cardUriString)\n\n  val cardNotificationUri = uriCreator.parse(s\"$baseUriNotificationString/$cardUriPath\")\n  val collectionNotificationUri =\n    uriCreator.parse(s\"$baseUriNotificationString/$collectionUriPath\")\n\n  def addCard(collectionId: Int, data: CardData): TaskService[Card] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(data) + (CardEntity.collectionId -> collectionId)\n\n        val id = contentResolverWrapper.insert(\n          uri = cardUri,\n          values = values,\n          notificationUris = Seq(\n            cardNotificationUri,\n            uriCreator.withAppendedPath(collectionNotificationUri, collectionId.toString)))\n\n        Card(id = id, data = data)\n      }\n    }\n\n  def addCards(datas: Seq[CardsWithCollectionId]): TaskService[Seq[Card]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = datas flatMap { dataWithCollectionId =>\n          dataWithCollectionId.data map { data =>\n            createMapValues(data) +\n              (CardEntity.collectionId -> dataWithCollectionId.collectionId)\n          }\n        }\n\n        val collectionNotificationUris = datas.map(_.collectionId).distinct.map { id =>\n          uriCreator.withAppendedPath(collectionNotificationUri, id.toString)\n        }\n\n        val ids = contentResolverWrapper.inserts(\n          authority = NineCardsUri.authorityPart,\n          uri = cardUri,\n          allValues = values,\n          notificationUris = collectionNotificationUris :+ cardNotificationUri)\n\n        (datas flatMap (_.data)) zip ids map {\n          case (data, id) => Card(id = id, data = data)\n        }\n      }\n    }\n\n  def deleteCards(maybeCollectionId: Option[Int] = None, where: String = \"\"): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val collectionUri = maybeCollectionId match {\n          case Some(id) if id != 0 =>\n            uriCreator.withAppendedPath(collectionNotificationUri, id.toString)\n          case _ => collectionNotificationUri\n        }\n        contentResolverWrapper.delete(\n          uri = cardUri,\n          where = where,\n          notificationUris = Seq(cardNotificationUri, collectionUri))\n      }\n    }\n\n  def deleteCard(collectionId: Int, cardId: Int): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.deleteById(\n          uri = cardUri,\n          id = cardId,\n          notificationUris = Seq(\n            cardNotificationUri,\n            uriCreator.withAppendedPath(collectionNotificationUri, collectionId.toString)))\n      }\n    }\n\n  def findCardById(id: Int): TaskService[Option[Card]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.findById(uri = cardUri, id = id, projection = allFields)(\n          getEntityFromCursor(cardEntityFromCursor)) map toCard\n      }\n    }\n\n  def fetchCardsByCollection(collectionId: Int): TaskService[Seq[Card]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.fetchAll(\n          uri = cardUri,\n          projection = allFields,\n          where = s\"${CardEntity.collectionId} = ?\",\n          whereParams = Seq(collectionId.toString),\n          orderBy = s\"${CardEntity.position} asc\")(getListFromCursor(cardEntityFromCursor)) map toCard\n      }\n    }\n\n  def fetchCards: TaskService[Seq[Card]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.fetchAll(uri = cardUri, projection = allFields)(\n          getListFromCursor(cardEntityFromCursor)) map toCard\n      }\n    }\n\n  def fetchIterableCards(\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\"): TaskService[IterableCursor[Card]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .getCursor(\n            uri = cardUri,\n            projection = allFields,\n            where = where,\n            whereParams = whereParams,\n            orderBy = orderBy)\n          .toIterator(cardFromCursor)\n      }\n    }\n\n  def updateCard(card: Card): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(card.data)\n\n        contentResolverWrapper.updateById(\n          uri = cardUri,\n          id = card.id,\n          values = values,\n          notificationUris = Seq(cardNotificationUri))\n      }\n    }\n\n  def updateCards(cards: Seq[Card]): TaskService[Seq[Int]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = cards map { card =>\n          (card.id, createMapValues(card.data))\n        }\n\n        contentResolverWrapper.updateByIds(\n          authority = NineCardsUri.authorityPart,\n          uri = cardUri,\n          idAndValues = values,\n          notificationUris = Seq(cardNotificationUri))\n      }\n    }\n\n  private[this] def createMapValues(data: CardData) =\n    Map[String, Any](\n      position     -> data.position,\n      term         -> data.term,\n      packageName  -> flatOrNull(data.packageName),\n      cardType     -> data.cardType,\n      intent       -> data.intent,\n      imagePath    -> flatOrNull(data.imagePath),\n      notification -> flatOrNull(data.notification))\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/repositories/CollectionRepository.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.repositories\n\nimport android.net.Uri\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.NotificationUri._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapper, UriCreator}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.IterableCursor\nimport cards.nine.models.IterableCursor._\nimport cards.nine.repository.Conversions.toCollection\nimport cards.nine.repository.model.{Collection, CollectionData}\nimport cards.nine.repository.provider.CollectionEntity.{allFields, position, _}\nimport cards.nine.repository.provider.NineCardsUri._\nimport cards.nine.repository.provider.{CollectionEntity, NineCardsUri}\nimport cards.nine.repository.repositories.RepositoryUtils._\nimport cards.nine.repository.{ImplicitsRepositoryExceptions, RepositoryException}\n\nimport scala.language.postfixOps\n\nclass CollectionRepository(contentResolverWrapper: ContentResolverWrapper, uriCreator: UriCreator)\n    extends ImplicitsRepositoryExceptions {\n\n  val collectionUri = uriCreator.parse(collectionUriString)\n\n  val collectionNotificationUri =\n    uriCreator.parse(s\"$baseUriNotificationString/$collectionUriPath\")\n\n  def addCollection(data: CollectionData): TaskService[Collection] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(data)\n\n        val id = contentResolverWrapper.insert(\n          uri = collectionUri,\n          values = values,\n          notificationUris = Seq(collectionNotificationUri))\n\n        Collection(id = id, data = data)\n      }\n    }\n\n  def addCollections(datas: Seq[CollectionData]): TaskService[Seq[Collection]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n\n        val values = datas map createMapValues\n\n        val ids = contentResolverWrapper.inserts(\n          authority = NineCardsUri.authorityPart,\n          uri = collectionUri,\n          allValues = values,\n          notificationUris = Seq(collectionNotificationUri))\n\n        datas zip ids map {\n          case (data, id) => Collection(id = id, data = data)\n        }\n      }\n    }\n\n  def deleteCollections(where: String = \"\"): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.delete(\n          uri = collectionUri,\n          where = where,\n          notificationUris = Seq(collectionNotificationUri))\n      }\n    }\n\n  def deleteCollection(collection: Collection): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.deleteById(\n          uri = collectionUri,\n          id = collection.id,\n          notificationUris = Seq(collectionNotificationUri))\n      }\n    }\n\n  def findCollectionById(id: Int): TaskService[Option[Collection]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.findById(uri = collectionUri, id = id, projection = allFields)(\n          getEntityFromCursor(collectionEntityFromCursor)) map toCollection\n      }\n    }\n\n  def fetchCollectionBySharedCollectionId(id: String): TaskService[Option[Collection]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        fetchCollection(selection = s\"$sharedCollectionId = ?\", selectionArgs = Seq(id.toString))\n      }\n    }\n\n  def fetchCollectionsBySharedCollectionIds(ids: Seq[String]): TaskService[Seq[Collection]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        fetchCollections(\n          selection = s\"$sharedCollectionId IN (${ids.map(id => s\"'$id'\").mkString(\",\")})\")\n      }\n    }\n\n  def fetchCollectionsByCategory(category: String): TaskService[Seq[Collection]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        fetchCollections(selection = s\"$appsCategory = ?\", selectionArgs = Seq(category))\n      }\n    }\n\n  def fetchCollectionByPosition(position: Int): TaskService[Option[Collection]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        fetchCollection(\n          selection = s\"${CollectionEntity.position} = ?\",\n          selectionArgs = Seq(position.toString))\n      }\n    }\n\n  def fetchIterableCollections(\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\"): TaskService[IterableCursor[Collection]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .getCursor(\n            uri = collectionUri,\n            projection = allFields,\n            where = where,\n            whereParams = whereParams,\n            orderBy = orderBy)\n          .toIterator(collectionFromCursor)\n      }\n    }\n\n  def fetchSortedCollections: TaskService[Seq[Collection]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        fetchCollections(sortOrder = s\"${CollectionEntity.position} asc\")\n      }\n    }\n\n  def updateCollection(collection: Collection): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(collection.data)\n\n        contentResolverWrapper.updateById(\n          uri = collectionUri,\n          id = collection.id,\n          values = values,\n          notificationUris = Seq(collectionNotificationUri))\n      }\n    }\n\n  def updateCollections(collections: Seq[Collection]): TaskService[Seq[Int]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = collections map { collection =>\n          (collection.id, createMapValues(collection.data))\n        }\n\n        contentResolverWrapper.updateByIds(\n          authority = NineCardsUri.authorityPart,\n          uri = collectionUri,\n          idAndValues = values,\n          notificationUris = Seq(collectionNotificationUri))\n      }\n    }\n\n  private[this] def fetchCollection(\n      uri: Uri = collectionUri,\n      projection: Seq[String] = allFields,\n      selection: String = \"\",\n      selectionArgs: Seq[String] = Seq.empty[String],\n      sortOrder: String = \"\") =\n    contentResolverWrapper.fetch(\n      uri = uri,\n      projection = projection,\n      where = selection,\n      whereParams = selectionArgs,\n      orderBy = sortOrder)(getEntityFromCursor(collectionEntityFromCursor)) map toCollection\n\n  private[this] def fetchCollections(\n      uri: Uri = collectionUri,\n      projection: Seq[String] = allFields,\n      selection: String = \"\",\n      selectionArgs: Seq[String] = Seq.empty[String],\n      sortOrder: String = \"\") =\n    contentResolverWrapper.fetchAll(\n      uri = uri,\n      projection = projection,\n      where = selection,\n      whereParams = selectionArgs,\n      orderBy = sortOrder)(getListFromCursor(collectionEntityFromCursor)) map toCollection\n\n  private[this] def createMapValues(data: CollectionData) =\n    Map[String, Any](\n      position                   -> data.position,\n      name                       -> data.name,\n      collectionType             -> data.collectionType,\n      icon                       -> data.icon,\n      themedColorIndex           -> data.themedColorIndex,\n      appsCategory               -> flatOrNull(data.appsCategory),\n      originalSharedCollectionId -> flatOrNull(data.originalSharedCollectionId),\n      sharedCollectionId         -> flatOrNull(data.sharedCollectionId),\n      sharedCollectionSubscribed -> data.sharedCollectionSubscribed.orNull)\n\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/repositories/DockAppRepository.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.repositories\n\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.NotificationUri._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapper, UriCreator}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.repository.Conversions.toDockApp\nimport cards.nine.repository.model.{DockApp, DockAppData}\nimport cards.nine.repository.provider.DockAppEntity._\nimport cards.nine.repository.provider.NineCardsUri._\nimport cards.nine.repository.provider.{DockAppEntity, NineCardsUri}\nimport cards.nine.repository.{ImplicitsRepositoryExceptions, RepositoryException}\n\nclass DockAppRepository(contentResolverWrapper: ContentResolverWrapper, uriCreator: UriCreator)\n    extends ImplicitsRepositoryExceptions {\n\n  val dockAppUri = uriCreator.parse(dockAppUriString)\n\n  val dockAppNotificationUri = uriCreator.parse(s\"$baseUriNotificationString/$dockAppUriPath\")\n\n  def addDockApp(data: DockAppData): TaskService[DockApp] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(data)\n\n        val id = contentResolverWrapper.insert(\n          uri = dockAppUri,\n          values = values,\n          notificationUris = Seq(dockAppNotificationUri))\n\n        DockApp(id = id, data = data)\n      }\n    }\n\n  def addDockApps(datas: Seq[DockAppData]): TaskService[Seq[DockApp]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n\n        val values = datas map createMapValues\n\n        val ids = contentResolverWrapper.inserts(\n          authority = NineCardsUri.authorityPart,\n          uri = dockAppUri,\n          allValues = values,\n          notificationUris = Seq(dockAppNotificationUri))\n\n        datas zip ids map {\n          case (data, id) => DockApp(id = id, data = data)\n        }\n      }\n    }\n\n  def deleteDockApps(where: String = \"\"): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .delete(uri = dockAppUri, where = where, notificationUris = Seq(dockAppNotificationUri))\n      }\n    }\n\n  def deleteDockApp(dockApp: DockApp): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.deleteById(\n          uri = dockAppUri,\n          id = dockApp.id,\n          notificationUris = Seq(dockAppNotificationUri))\n      }\n    }\n\n  def findDockAppById(id: Int): TaskService[Option[DockApp]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.findById(uri = dockAppUri, id = id, projection = allFields)(\n          getEntityFromCursor(dockAppEntityFromCursor)) map toDockApp\n      }\n    }\n\n  def fetchDockApps(\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = s\"${DockAppEntity.position} asc\"): TaskService[Seq[DockApp]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.fetchAll(\n          uri = dockAppUri,\n          projection = allFields,\n          where = where,\n          whereParams = whereParams,\n          orderBy = orderBy)(getListFromCursor(dockAppEntityFromCursor)) map toDockApp\n      }\n    }\n\n  def updateDockApp(item: DockApp): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(item.data)\n\n        contentResolverWrapper.updateById(\n          uri = dockAppUri,\n          id = item.id,\n          values = values,\n          notificationUris = Seq(dockAppNotificationUri))\n      }\n    }\n\n  def updateDockApps(items: Seq[DockApp]): TaskService[Seq[Int]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = items map { item =>\n          (item.id, createMapValues(item.data))\n        }\n\n        contentResolverWrapper.updateByIds(\n          authority = NineCardsUri.authorityPart,\n          uri = dockAppUri,\n          idAndValues = values)\n      }\n    }\n\n  private[this] def createMapValues(data: DockAppData) =\n    Map[String, Any](\n      name      -> data.name,\n      dockType  -> data.dockType,\n      intent    -> data.intent,\n      imagePath -> data.imagePath,\n      position  -> data.position)\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/repositories/MomentRepository.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.repositories\n\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.NotificationUri._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapper, UriCreator}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.IterableCursor\nimport cards.nine.models.IterableCursor._\nimport cards.nine.repository.Conversions.toMoment\nimport cards.nine.repository.model.{Moment, MomentData}\nimport cards.nine.repository.provider.MomentEntity._\nimport cards.nine.repository.provider.NineCardsUri._\nimport cards.nine.repository.provider.{MomentEntity, NineCardsUri}\nimport cards.nine.repository.repositories.RepositoryUtils._\nimport cards.nine.repository.{ImplicitsRepositoryExceptions, RepositoryException}\n\nimport scala.language.postfixOps\n\nclass MomentRepository(contentResolverWrapper: ContentResolverWrapper, uriCreator: UriCreator)\n    extends ImplicitsRepositoryExceptions {\n\n  val momentUri = uriCreator.parse(momentUriString)\n\n  val momentNotificationUri = uriCreator.parse(s\"$baseUriNotificationString/$momentUriPath\")\n\n  def addMoment(data: MomentData): TaskService[Moment] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(data)\n\n        val id = contentResolverWrapper\n          .insert(uri = momentUri, values = values, notificationUris = Seq(momentNotificationUri))\n\n        Moment(id = id, data = data)\n      }\n    }\n\n  def addMoments(datas: Seq[MomentData]): TaskService[Seq[Moment]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n\n        val values = datas map createMapValues\n\n        val ids = contentResolverWrapper.inserts(\n          authority = NineCardsUri.authorityPart,\n          uri = momentUri,\n          allValues = values,\n          notificationUris = Seq(momentNotificationUri))\n\n        datas zip ids map {\n          case (data, id) => Moment(id = id, data = data)\n        }\n      }\n    }\n\n  def deleteMoments(where: String = \"\"): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .delete(uri = momentUri, where = where, notificationUris = Seq(momentNotificationUri))\n      }\n    }\n\n  def deleteMoment(id: Int): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .deleteById(uri = momentUri, id = id, notificationUris = Seq(momentNotificationUri))\n      }\n    }\n\n  def findMomentById(id: Int): TaskService[Option[Moment]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.findById(uri = momentUri, id = id, projection = allFields)(\n          getEntityFromCursor(momentEntityFromCursor)) map toMoment\n      }\n    }\n\n  def fetchMomentByCollectionId(collectionId: Int): TaskService[Option[Moment]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.fetch(\n          uri = momentUri,\n          projection = allFields,\n          where = s\"${MomentEntity.collectionId} = ?\",\n          whereParams = Seq(collectionId.toString),\n          orderBy = \"\")(getEntityFromCursor(momentEntityFromCursor)) map toMoment\n      }\n    }\n\n  def fetchMoments(\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\"): TaskService[Seq[Moment]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.fetchAll(\n          uri = momentUri,\n          projection = allFields,\n          where = where,\n          whereParams = whereParams,\n          orderBy = orderBy)(getListFromCursor(momentEntityFromCursor)) map toMoment\n      }\n    }\n\n  def fetchIterableMoments(\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\"): TaskService[IterableCursor[Moment]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .getCursor(\n            uri = momentUri,\n            projection = allFields,\n            where = where,\n            whereParams = whereParams,\n            orderBy = orderBy)\n          .toIterator(momentFromCursor)\n      }\n    }\n\n  def updateMoment(item: Moment): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(item.data)\n\n        contentResolverWrapper.updateById(\n          uri = momentUri,\n          id = item.id,\n          values = values,\n          notificationUris = Seq(momentNotificationUri))\n      }\n    }\n\n  private[this] def createMapValues(data: MomentData) =\n    Map[String, Any](\n      collectionId -> (data.collectionId orNull),\n      timeslot     -> data.timeslot,\n      wifi         -> data.wifi,\n      bluetooth    -> data.bluetooth,\n      headphone    -> data.headphone,\n      momentType   -> flatOrNull(data.momentType))\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/repositories/RepositoryUtils.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.repositories\n\nimport cards.nine.commons.javaNull\n\nobject RepositoryUtils {\n\n  // We have a bug in server and sometimes we have a empty strings.\n  // We are going to fix this problem here\n  def flatOrNull(in: Option[String]): String =\n    in map { value =>\n      if (value == \"\") javaNull else value\n    } orNull\n\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/repositories/UserRepository.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.repositories\n\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.NotificationUri._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapper, UriCreator}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.IterableCursor\nimport cards.nine.models.IterableCursor._\nimport cards.nine.repository.Conversions.toUser\nimport cards.nine.repository.model.{User, UserData}\nimport cards.nine.repository.provider.NineCardsUri._\nimport cards.nine.repository.provider.UserEntity._\nimport cards.nine.repository.repositories.RepositoryUtils._\nimport cards.nine.repository.{ImplicitsRepositoryExceptions, RepositoryException}\n\nimport scala.language.postfixOps\n\nclass UserRepository(contentResolverWrapper: ContentResolverWrapper, uriCreator: UriCreator)\n    extends ImplicitsRepositoryExceptions {\n\n  val userUri = uriCreator.parse(userUriString)\n\n  val userNotificationUri = uriCreator.parse(s\"$baseUriNotificationString/$userUriPath\")\n\n  def addUser(data: UserData): TaskService[User] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(data)\n\n        val id = contentResolverWrapper\n          .insert(uri = userUri, values = values, notificationUris = Seq(userNotificationUri))\n\n        User(id = id, data = data)\n      }\n    }\n\n  def deleteUsers(where: String = \"\"): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .delete(uri = userUri, where = where, notificationUris = Seq(userNotificationUri))\n      }\n    }\n\n  def deleteUser(user: User): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .deleteById(uri = userUri, id = user.id, notificationUris = Seq(userNotificationUri))\n      }\n    }\n\n  def findUserById(id: Int): TaskService[Option[User]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.findById(uri = userUri, id = id, projection = allFields)(\n          getEntityFromCursor(userEntityFromCursor)) map toUser\n      }\n    }\n\n  def fetchUsers: TaskService[Seq[User]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.fetchAll(uri = userUri, projection = allFields)(\n          getListFromCursor(userEntityFromCursor)) map toUser\n      }\n    }\n\n  def fetchIterableUsers(\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\"): TaskService[IterableCursor[User]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .getCursor(\n            uri = userUri,\n            projection = allFields,\n            where = where,\n            whereParams = whereParams,\n            orderBy = orderBy)\n          .toIterator(userFromCursor)\n      }\n    }\n\n  def updateUser(item: User): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(item.data)\n\n        contentResolverWrapper.updateById(\n          uri = userUri,\n          id = item.id,\n          values = values,\n          notificationUris = Seq(userNotificationUri))\n      }\n    }\n\n  private[this] def createMapValues(data: UserData) =\n    Map[String, Any](\n      email         -> flatOrNull(data.email),\n      apiKey        -> flatOrNull(data.apiKey),\n      sessionToken  -> flatOrNull(data.sessionToken),\n      deviceToken   -> flatOrNull(data.deviceToken),\n      marketToken   -> flatOrNull(data.marketToken),\n      name          -> flatOrNull(data.name),\n      avatar        -> flatOrNull(data.avatar),\n      cover         -> flatOrNull(data.cover),\n      deviceName    -> flatOrNull(data.deviceName),\n      deviceCloudId -> flatOrNull(data.deviceCloudId))\n}\n"
  },
  {
    "path": "modules/repository/src/main/scala/cards/nine/repository/repositories/WidgetRepository.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.repositories\n\nimport android.net.Uri\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.NotificationUri._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapper, UriCreator}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.IterableCursor\nimport cards.nine.models.IterableCursor._\nimport cards.nine.repository.Conversions.toWidget\nimport cards.nine.repository.model.{Widget, WidgetData}\nimport cards.nine.repository.provider.NineCardsUri._\nimport cards.nine.repository.provider.WidgetEntity._\nimport cards.nine.repository.provider.{NineCardsUri, WidgetEntity}\nimport cards.nine.repository.{ImplicitsRepositoryExceptions, RepositoryException}\n\nimport scala.language.postfixOps\n\nclass WidgetRepository(contentResolverWrapper: ContentResolverWrapper, uriCreator: UriCreator)\n    extends ImplicitsRepositoryExceptions {\n\n  val widgetUri = uriCreator.parse(widgetUriString)\n\n  val widgetNotificationUri = uriCreator.parse(s\"$baseUriNotificationString/$widgetUriPath\")\n\n  def addWidget(data: WidgetData): TaskService[Widget] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(data)\n\n        val id = contentResolverWrapper\n          .insert(uri = widgetUri, values = values, notificationUris = Seq(widgetNotificationUri))\n\n        Widget(id = id, data = data)\n      }\n    }\n\n  def addWidgets(datas: Seq[WidgetData]): TaskService[Seq[Widget]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n\n        val values = datas map createMapValues\n\n        val ids = contentResolverWrapper.inserts(\n          authority = NineCardsUri.authorityPart,\n          uri = widgetUri,\n          allValues = values,\n          notificationUris = Seq(widgetNotificationUri))\n\n        datas zip ids map {\n          case (data, id) => Widget(id = id, data = data)\n        }\n      }\n    }\n\n  def deleteWidgets(where: String = \"\"): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .delete(uri = widgetUri, where = where, notificationUris = Seq(widgetNotificationUri))\n      }\n    }\n\n  def deleteWidget(widget: Widget): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.deleteById(\n          uri = widgetUri,\n          id = widget.id,\n          notificationUris = Seq(widgetNotificationUri))\n      }\n    }\n\n  def findWidgetById(id: Int): TaskService[Option[Widget]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.findById(uri = widgetUri, id = id, projection = allFields)(\n          getEntityFromCursor(widgetEntityFromCursor)) map toWidget\n      }\n    }\n\n  def fetchWidgetByAppWidgetId(appWidgetId: Int): TaskService[Option[Widget]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        fetchWidget(\n          selection = s\"${WidgetEntity.appWidgetId} = ?\",\n          selectionArgs = Seq(appWidgetId.toString))\n      }\n    }\n\n  def fetchWidgetsByMoment(momentId: Int): TaskService[Seq[Widget]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.fetchAll(\n          uri = widgetUri,\n          projection = allFields,\n          where = s\"${WidgetEntity.momentId} = ?\",\n          whereParams = Seq(momentId.toString),\n          orderBy = s\"${WidgetEntity.momentId} asc\")(getListFromCursor(widgetEntityFromCursor)) map toWidget\n      }\n    }\n\n  def fetchWidgets(\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\"): TaskService[Seq[Widget]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper.fetchAll(\n          uri = widgetUri,\n          projection = allFields,\n          where = where,\n          whereParams = whereParams,\n          orderBy = orderBy)(getListFromCursor(widgetEntityFromCursor)) map toWidget\n      }\n    }\n\n  def fetchIterableWidgets(\n      where: String = \"\",\n      whereParams: Seq[String] = Seq.empty,\n      orderBy: String = \"\"): TaskService[IterableCursor[Widget]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        contentResolverWrapper\n          .getCursor(\n            uri = widgetUri,\n            projection = allFields,\n            where = where,\n            whereParams = whereParams,\n            orderBy = orderBy)\n          .toIterator(widgetFromCursor)\n      }\n    }\n\n  def updateWidget(widget: Widget): TaskService[Int] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = createMapValues(widget.data)\n\n        contentResolverWrapper.updateById(\n          uri = widgetUri,\n          id = widget.id,\n          values = values,\n          notificationUris = Seq(widgetNotificationUri))\n      }\n    }\n\n  def updateWidgets(widgets: Seq[Widget]): TaskService[Seq[Int]] =\n    TaskService {\n      CatchAll[RepositoryException] {\n        val values = widgets map { widget =>\n          (widget.id, createMapValues(widget.data))\n        }\n\n        contentResolverWrapper.updateByIds(\n          authority = NineCardsUri.authorityPart,\n          uri = widgetUri,\n          idAndValues = values,\n          notificationUris = Seq(widgetNotificationUri))\n      }\n    }\n\n  private[this] def fetchWidget(\n      uri: Uri = widgetUri,\n      projection: Seq[String] = allFields,\n      selection: String = \"\",\n      selectionArgs: Seq[String] = Seq.empty[String],\n      sortOrder: String = \"\") =\n    contentResolverWrapper.fetch(\n      uri = uri,\n      projection = projection,\n      where = selection,\n      whereParams = selectionArgs,\n      orderBy = sortOrder)(getEntityFromCursor(widgetEntityFromCursor)) map toWidget\n\n  private[this] def createMapValues(data: WidgetData) =\n    Map[String, Any](\n      momentId    -> data.momentId,\n      packageName -> data.packageName,\n      className   -> data.className,\n      appWidgetId -> data.appWidgetId,\n      startX      -> data.startX,\n      startY      -> data.startY,\n      spanX       -> data.spanX,\n      spanY       -> data.spanY,\n      widgetType  -> data.widgetType,\n      label       -> (data.label orNull),\n      imagePath   -> (data.imagePath orNull),\n      intent      -> (data.intent orNull))\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/app/AppRepositorySpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.app\n\nimport android.net.Uri\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapperImpl, UriCreator}\nimport cards.nine.commons.services.TaskService.NineCardException\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.repository.{IntDataType, LongDataType, MockCursor, StringDataType}\nimport cards.nine.models.IterableCursor\nimport cards.nine.models.IterableCursor._\nimport cards.nine.repository.RepositoryException\nimport cards.nine.repository.model.App\nimport cards.nine.repository.provider.AppEntity.{name, packageName, _}\nimport cards.nine.repository.provider._\nimport cards.nine.repository.repositories._\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait AppRepositorySpecification\n    extends Specification\n    with DisjunctionMatchers\n    with Mockito\n    with AppRepositoryTestData {\n\n  val contentResolverException = new RuntimeException(\"Irrelevant message\")\n\n  trait AppRepositoryScope extends Scope {\n\n    lazy val contentResolverWrapper = mock[ContentResolverWrapperImpl]\n\n    lazy val uriCreator = mock[UriCreator]\n\n    lazy val appRepository = new AppRepository(contentResolverWrapper, uriCreator) {\n      override protected def getNamesAlphabetically: Seq[String] = appsDataSequence\n\n      override protected def getCategoriesAlphabetically: Seq[String] = categoryDataSequence\n\n      override protected def getInstallationDate: Seq[Long] = installationDateDataSequence\n    }\n\n    lazy val mockUri = mock[Uri]\n\n    uriCreator.parse(any) returns mockUri\n  }\n\n  trait ErrorCounterAppRepositoryResponses { self: AppRepositoryScope =>\n\n    lazy val appRepositoryException = new AppRepository(contentResolverWrapper, uriCreator) {\n      override protected def getNamesAlphabetically: Seq[String] =\n        throw contentResolverException\n\n      override protected def getCategoriesAlphabetically: Seq[String] =\n        throw contentResolverException\n\n      override protected def getInstallationDate: Seq[Long] =\n        throw contentResolverException\n    }\n  }\n\n}\n\ntrait AppMockCursor extends MockCursor with AppRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, appSeq map (_.id), IntDataType),\n    (name, 1, appSeq map (_.data.name), StringDataType),\n    (packageName, 2, appSeq map (_.data.packageName), StringDataType),\n    (className, 3, appSeq map (_.data.className), StringDataType),\n    (category, 4, appSeq map (_.data.category), StringDataType),\n    (dateInstalled, 7, appSeq map (_.data.dateInstalled), LongDataType),\n    (dateUpdate, 8, appSeq map (_.data.dateUpdate), LongDataType),\n    (version, 9, appSeq map (_.data.version), StringDataType),\n    (installedFromGooglePlay,\n     10,\n     appSeq map (item => if (item.data.installedFromGooglePlay) 1 else 0),\n     IntDataType))\n\n  prepareCursor[App](appSeq.size, cursorData)\n}\n\ntrait EmptyAppMockCursor extends MockCursor with AppRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, Seq.empty, IntDataType),\n    (name, 1, Seq.empty, StringDataType),\n    (packageName, 2, Seq.empty, StringDataType),\n    (className, 3, Seq.empty, StringDataType),\n    (category, 4, Seq.empty, StringDataType),\n    (dateInstalled, 7, Seq.empty, LongDataType),\n    (dateUpdate, 8, Seq.empty, LongDataType),\n    (version, 9, Seq.empty, StringDataType),\n    (installedFromGooglePlay, 10, Seq.empty, IntDataType))\n\n  prepareCursor[App](0, cursorData)\n}\n\nclass AppRepositorySpec extends AppRepositorySpecification {\n\n  \"AppRepositoryClient component\" should {\n\n    \"addApp\" should {\n\n      \"return an App object with a valid request\" in\n        new AppRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) returns testAppId\n          val result = appRepository.addApp(data = createAppData).value.run\n\n          result must beLike {\n            case Right(appResult) =>\n              appResult.id shouldEqual testAppId\n              appResult.data.packageName shouldEqual testPackageName\n          }\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new AppRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) throws contentResolverException\n          val result = appRepository.addApp(data = createAppData).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"addApps\" should {\n\n      \"return a sequence of addApp objects with a valid request\" in\n        new AppRepositoryScope {\n\n          contentResolverWrapper.inserts(any, any, any, any) returns appIdSeq\n          val result = appRepository.addApps(datas = appDataSeq).value.run\n          result shouldEqual Right((): Unit)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new AppRepositoryScope {\n\n          contentResolverWrapper.inserts(any, any, any, any) throws contentResolverException\n          val result = appRepository.addApps(datas = appDataSeq).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n\n    }\n\n    \"fetchAlphabeticalAppsCounter\" should {\n\n      \"return a sequence of DataCounter sort alphabetically\" in\n        new AppRepositoryScope {\n\n          val result = appRepository.fetchAlphabeticalAppsCounter.value.run\n          result shouldEqual Right(appsDataCounters)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new AppRepositoryScope with ErrorCounterAppRepositoryResponses {\n\n          val result = appRepositoryException.fetchAlphabeticalAppsCounter.value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n  }\n\n  \"fetchCategorizedAppsCounter\" should {\n\n    \"return a sequence of DataCounter sort by category\" in\n      new AppRepositoryScope {\n\n        val result = appRepository.fetchCategorizedAppsCounter.value.run\n        result shouldEqual Right(categoryDataCounters)\n      }\n\n    \"return a RepositoryException when a exception is thrown\" in\n      new AppRepositoryScope with ErrorCounterAppRepositoryResponses {\n\n        val result = appRepositoryException.fetchCategorizedAppsCounter.value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"fetchInstallationDateAppsCounter\" should {\n\n    \"return a sequence of DataCounter sort by installation date\" in\n      new AppRepositoryScope {\n\n        val result = appRepository.fetchInstallationDateAppsCounter.value.run\n        result shouldEqual Right(installationDateDateCounters)\n      }\n\n    \"return a RepositoryException when a exception is thrown\" in\n      new AppRepositoryScope with ErrorCounterAppRepositoryResponses {\n\n        val result = appRepositoryException.fetchInstallationDateAppsCounter.value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"deleteApps\" should {\n\n    \"return a successful response when all the apps are deleted\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.delete(any, any, any, any) returns 1\n        val result = appRepository.deleteApps().value.run\n        result shouldEqual Right(1)\n      }\n\n    \"return a RepositoryException when a exception is thrown\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.delete(any, any, any, any) throws contentResolverException\n        val result = appRepository.deleteApps().value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"deleteApp\" should {\n\n    \"return a successful response when a valid app id is given\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.deleteById(any, any, any, any, any) returns 1\n        val result = appRepository.deleteApp(app = app).value.run\n        result shouldEqual Right(1)\n      }\n\n    \"return a RepositoryException when a exception is thrown\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.deleteById(any, any, any, any, any) throws contentResolverException\n        val result = appRepository.deleteApp(app = app).value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"deleteAppByPackage\" should {\n\n    \"return a successful response when a valid package name is given\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.delete(any, any, any, any) returns 1\n        val result = appRepository.deleteAppByPackage(packageName = testPackageName).value.run\n        result shouldEqual Right(1)\n      }\n\n    \"return a RepositoryException when a exception is thrown\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.delete(any, any, any, any) throws contentResolverException\n        val result = appRepository.deleteAppByPackage(packageName = testPackageName).value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"fetchApps\" should {\n\n    \"return all the apps stored in the database\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.fetchAll[AppEntity](any, any, any, any, any)(any) returns appEntitySeq\n\n        val result = appRepository.fetchApps().value.run\n        result shouldEqual Right(appSeq)\n\n      }\n\n    \"return a RepositoryException when a exception is thrown\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.fetchAll[AppEntity](any, any, any, any, any)(any) throws contentResolverException\n\n        val result = appRepository.fetchApps().value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"fetchIterableApps\" should {\n\n    \"return an IterableCursor[App] \" in\n      new AppMockCursor with AppRepositoryScope {\n\n        contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n\n        val result = appRepository.fetchIterableApps(where = testMockWhere).value.run\n\n        result must beLike {\n          case Right(iterator) =>\n            toSeq(iterator) shouldEqual appSeq\n        }\n\n        there was one(contentResolverWrapper)\n          .getCursor(mockUri, AppEntity.allFields, testMockWhere, Seq.empty, \"\")\n      }\n\n    \"return an a RepositoryException when a exception is thrown \" in\n      new AppMockCursor with AppRepositoryScope {\n\n        contentResolverWrapper.getCursor(any, any, any, any, any) throws contentResolverException\n\n        val result = appRepository.fetchIterableApps(where = testMockWhere).value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"findAppById\" should {\n\n    \"return an App object when a existent id is given\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.findById[AppEntity](any, any, any, any, any, any)(any) returns Some(\n          appEntity)\n        val result = appRepository.findAppById(id = testAppId).value.run\n\n        result must beLike {\n          case Right(maybeApp) =>\n            maybeApp must beSome[App].which { app =>\n              app.id shouldEqual testAppId\n              app.data.packageName shouldEqual testPackageName\n            }\n        }\n      }\n\n    \"return None when a non-existent id is given\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.findById(any, any, any, any, any, any)(any) returns None\n        val result = appRepository.findAppById(id = testNonExistingAppId).value.run\n        result shouldEqual Right(None)\n      }\n\n    \"return a RepositoryException when a exception is thrown\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.findById(any, any, any, any, any, any)(any) throws contentResolverException\n        val result = appRepository.findAppById(id = testAppId).value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"fetchAppByPackage\" should {\n    \"return an App object when a existent package name is given\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.fetch[AppEntity](any, any, any, any, any)(any) returns Some(\n          appEntity)\n\n        val result = appRepository.fetchAppByPackage(packageName = testPackageName).value.run\n\n        result must beLike {\n          case Right(maybeApp) =>\n            maybeApp must beSome[App].which { app =>\n              app.id shouldEqual testAppId\n              app.data.packageName shouldEqual testPackageName\n            }\n        }\n      }\n\n    \"return None when a non-existent package name is given\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.fetch[AppEntity](any, any, any, any, any)(any) returns None\n\n        val result =\n          appRepository.fetchAppByPackage(packageName = testNonExistingPackageName).value.run\n        result shouldEqual Right(None)\n      }\n\n    \"return a RepositoryException when a exception is thrown\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.fetch[AppEntity](any, any, any, any, any)(any) throws contentResolverException\n\n        val result = appRepository.fetchAppByPackage(packageName = testPackageName).value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"fecthAppByPackages\" should {\n\n    \"return a sequence of App when a existent\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.fetchAll[AppEntity](any, any, any, any, any)(any) returns appEntitySeq\n\n        val result: Either[NineCardException, Seq[App]] =\n          appRepository.fetchAppByPackages(Seq(testPackageName)).value.run\n        result shouldEqual Right(appSeq)\n      }\n\n    \"return a RepositoryException when a exception is thrown\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.fetchAll[AppEntity](any, any, any, any, any)(any) throws contentResolverException\n\n        val result: Either[NineCardException, Seq[App]] =\n          appRepository.fetchAppByPackages(Seq(testPackageName)).value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"fetchAppsByCategory\" should {\n\n    \"return a sequence of Apps when a existent category is given\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.fetchAll[AppEntity](any, any, any, any, any)(any) returns appEntitySeq\n\n        val result = appRepository.fetchAppsByCategory(category = testCategory).value.run\n        result shouldEqual Right(appSeq)\n      }\n\n    \"return an empty sequence when a non-existent category is given\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.fetchAll[AppEntity](any, any, any, any, any)(any) returns Seq.empty\n\n        val result =\n          appRepository.fetchAppsByCategory(category = testNonExistingCategory).value.run\n        result shouldEqual Right(Seq.empty)\n\n      }\n\n    \"return a RepositoryException when a exception is thrown\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.fetchAll[AppEntity](any, any, any, any, any)(any) throws contentResolverException\n\n        val result = appRepository.fetchAppsByCategory(category = testCategory).value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"fetchIterableAppsByCategory\" should {\n\n    \"return an IterableCursor of App when a existent category is given\" in\n      new AppMockCursor with AppRepositoryScope {\n\n        contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n\n        val result = appRepository.fetchIterableAppsByCategory(category = testCategory).value.run\n\n        result must beLike {\n          case Right(iterator) =>\n            toSeq(iterator) shouldEqual appSeq\n        }\n\n        there was one(contentResolverWrapper)\n          .getCursor(mockUri, AppEntity.allFields, s\"$category = ?\", Seq(testCategory), \"\")\n      }\n\n    \"return an IterableCursor of App when a non-existent category is given\" in\n      new EmptyAppMockCursor with AppRepositoryScope {\n\n        contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n\n        val result =\n          appRepository.fetchIterableAppsByCategory(category = testNonExistingCategory).value.run\n        result must beLike {\n          case Right(iterator) =>\n            toSeq(iterator) shouldEqual Seq.empty\n        }\n\n        there was one(contentResolverWrapper).getCursor(\n          mockUri,\n          AppEntity.allFields,\n          s\"$category = ?\",\n          Seq(testNonExistingCategory),\n          \"\")\n      }\n\n    \"return an a RepositoryException when a exception is thrown \" in\n      new AppMockCursor with AppRepositoryScope {\n\n        contentResolverWrapper.getCursor(any, any, any, any, any) throws contentResolverException\n\n        val result = appRepository.fetchIterableAppsByCategory(category = testCategory).value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"updateApp\" should {\n\n    \"return a successful response when the app is updated\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.updateById(any, any, any, any) returns 1\n        val result = appRepository.updateApp(app = app).value.run\n        result shouldEqual Right(1)\n      }\n\n    \"return a RepositoryException when a exception is thrown\" in\n      new AppRepositoryScope {\n\n        contentResolverWrapper.updateById(any, any, any, any) throws contentResolverException\n        val result = appRepository.updateApp(app = app).value.run\n        result must beAnInstanceOf[Left[RepositoryException, _]]\n      }\n  }\n\n  \"getEntityFromCursor\" should {\n\n    \"return None when an empty cursor is given\" in\n      new EmptyAppMockCursor with AppRepositoryScope {\n\n        val result = getEntityFromCursor(appEntityFromCursor)(mockCursor)\n        result must beNone\n      }\n\n    \"return an App object when a cursor with data is given\" in\n      new AppMockCursor with AppRepositoryScope {\n\n        val result = getEntityFromCursor(appEntityFromCursor)(mockCursor)\n\n        result must beSome[AppEntity].which { app =>\n          app.id shouldEqual appEntity.id\n          app.data shouldEqual appEntity.data\n        }\n      }\n  }\n\n  \"getListFromCursor\" should {\n\n    \"return an empty sequence when an empty cursor is given\" in\n      new EmptyAppMockCursor with AppRepositoryScope {\n\n        val result = getListFromCursor(appEntityFromCursor)(mockCursor)\n        result should beEmpty\n      }\n\n    \"return an App sequence when a cursor with data is given\" in\n      new AppMockCursor with AppRepositoryScope {\n\n        val result = getListFromCursor(appEntityFromCursor)(mockCursor)\n        result shouldEqual appEntitySeq\n      }\n  }\n\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/app/AppRepositoryTestData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.app\n\nimport cards.nine.repository.model.{App, AppData, DataCounter}\nimport cards.nine.repository.provider.{AppEntity, AppEntityData}\nimport org.joda.time.DateTime\n\nimport scala.util.Random\n\ntrait AppRepositoryTestData {\n\n  val testAppId                   = Random.nextInt(10)\n  val testNonExistingAppId        = 15\n  val testName                    = Random.nextString(5)\n  val testPackageName             = Random.nextString(5)\n  val testNonExistingPackageName  = Random.nextString(5)\n  val testClassName               = Random.nextString(5)\n  val testCategory                = Random.nextString(5)\n  val testNonExistingCategory     = Random.nextString(5)\n  val testImagePath               = Random.nextString(5)\n  val testDateInstalled           = Random.nextLong()\n  val testDateUpdate              = Random.nextLong()\n  val testVersion                 = Random.nextString(5)\n  val testInstalledFromGooglePlay = Random.nextBoolean()\n  val testMockWhere               = \"mocked-where\"\n\n  val appEntitySeq = createAppEntitySeq(5)\n  val appEntity    = appEntitySeq(0)\n  val appSeq       = createAppSeq(5)\n  val app          = appSeq(0)\n  val appIdSeq     = appSeq map (_.id)\n  val appDataSeq   = appSeq map (_.data)\n\n  def createAppEntitySeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        AppEntity(\n          id = testAppId + i,\n          data = AppEntityData(\n            name = testName,\n            packageName = testPackageName,\n            className = testClassName,\n            category = testCategory,\n            dateInstalled = testDateInstalled,\n            dateUpdate = testDateUpdate,\n            version = testVersion,\n            installedFromGooglePlay = testInstalledFromGooglePlay)))\n\n  def createAppSeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        App(\n          id = testAppId + i,\n          data = AppData(\n            name = testName,\n            packageName = testPackageName,\n            className = testClassName,\n            category = testCategory,\n            dateInstalled = testDateInstalled,\n            dateUpdate = testDateUpdate,\n            version = testVersion,\n            installedFromGooglePlay = testInstalledFromGooglePlay)))\n\n  def createAppValues =\n    Map[String, Any](\n      AppEntity.name                    -> testName,\n      AppEntity.packageName             -> testPackageName,\n      AppEntity.className               -> testClassName,\n      AppEntity.category                -> testCategory,\n      AppEntity.dateInstalled           -> testDateInstalled,\n      AppEntity.dateUpdate              -> testDateUpdate,\n      AppEntity.version                 -> testVersion,\n      AppEntity.installedFromGooglePlay -> testInstalledFromGooglePlay)\n\n  def createAppData =\n    AppData(\n      name = testName,\n      packageName = testPackageName,\n      className = testClassName,\n      category = testCategory,\n      dateInstalled = testDateInstalled,\n      dateUpdate = testDateUpdate,\n      version = testVersion,\n      installedFromGooglePlay = testInstalledFromGooglePlay)\n\n  val appsDataSequence: Seq[String] =\n    Seq(\"!aaa\", \"2bbb\", \"?ccc\", \"1ddd\", \"#eeee\", \"Abc\", \"Acd\", \"Ade\", \"Bcd\", \"Bde\", \"Bef\", \"Cde\")\n\n  val appsDataCounters =\n    Seq(DataCounter(\"#\", 5), DataCounter(\"A\", 3), DataCounter(\"B\", 3), DataCounter(\"C\", 1))\n\n  val categoryDataSequence: Seq[String] =\n    Seq(\n      \"COMMUNICATION\",\n      \"COMMUNICATION\",\n      \"COMMUNICATION\",\n      \"GAME_SIMULATION\",\n      \"GAME_BOARD\",\n      \"GAME_CARD\",\n      \"SOCIAL\",\n      \"SOCIAL\",\n      \"SOCIAL\",\n      \"SOCIAL\",\n      \"TOOLS\",\n      \"TOOLS\")\n\n  val categoryDataCounters = Seq(\n    DataCounter(\"COMMUNICATION\", 3),\n    DataCounter(\"GAME\", 3),\n    DataCounter(\"SOCIAL\", 4),\n    DataCounter(\"TOOLS\", 2))\n\n  val now = new DateTime()\n\n  val installationDateDataSequence: Seq[Long] = Seq(\n    now.minusDays(1).getMillis,\n    now.minusDays(2).getMillis,\n    now.minusWeeks(1).minusDays(1).getMillis,\n    now.minusWeeks(1).minusDays(3).getMillis,\n    now.minusWeeks(2).minusDays(4).getMillis,\n    now.minusMonths(1).minusWeeks(1).getMillis,\n    now.minusMonths(2).minusWeeks(1).getMillis,\n    now.minusMonths(2).minusWeeks(1).minusDays(3).getMillis)\n\n  val installationDateDateCounters = Seq(\n    DataCounter(\"oneWeek\", 2),\n    DataCounter(\"twoWeeks\", 2),\n    DataCounter(\"oneMonth\", 1),\n    DataCounter(\"twoMonths\", 1),\n    DataCounter(\"fourMonths\", 2))\n\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/card/CardRepositorySpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.card\n\nimport android.net.Uri\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapperImpl, UriCreator}\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.repository.{IntDataType, MockCursor, StringDataType}\nimport cards.nine.models.IterableCursor\nimport cards.nine.models.IterableCursor._\nimport cards.nine.repository.RepositoryException\nimport cards.nine.repository.model.Card\nimport cards.nine.repository.provider.CardEntity.{allFields, position, _}\nimport cards.nine.repository.provider._\nimport cards.nine.repository.repositories._\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\nimport scala.language.postfixOps\n\ntrait CardRepositorySpecification extends Specification with DisjunctionMatchers with Mockito {\n\n  trait CardRepositoryScope extends Scope with CardRepositoryTestData {\n\n    lazy val contentResolverWrapper = mock[ContentResolverWrapperImpl]\n\n    lazy val uriCreator = mock[UriCreator]\n\n    lazy val cardRepository = new CardRepository(contentResolverWrapper, uriCreator)\n\n    lazy val mockUri = mock[Uri]\n\n    lazy val mockUriBuilt = mock[Uri]\n\n    lazy val mockUriBuilder = mock[Uri.Builder]\n\n    uriCreator.parse(any) returns mockUri\n\n    uriCreator.withAppendedPath(any, any) returns mockUriBuilt\n\n    val contentResolverException = new RuntimeException(\"Irrelevant message\")\n  }\n\n}\n\ntrait CardMockCursor extends MockCursor with CardRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, cardSeq map (_.id), IntDataType),\n    (position, 1, cardSeq map (_.data.position), IntDataType),\n    (collectionId, 2, cardSeq map (_ => testCollectionId), IntDataType),\n    (term, 3, cardSeq map (_.data.term), StringDataType),\n    (packageName, 4, cardSeq map (_.data.packageName orNull), StringDataType),\n    (cardType, 5, cardSeq map (_.data.cardType), StringDataType),\n    (intent, 6, cardSeq map (_.data.intent), StringDataType),\n    (imagePath, 7, cardSeq map (_.data.imagePath orNull), StringDataType),\n    (notification, 11, cardSeq map (_.data.notification orNull), StringDataType)\n  )\n\n  prepareCursor[Card](cardSeq.size, cursorData)\n}\n\ntrait EmptyCardMockCursor extends MockCursor with CardRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, Seq.empty, IntDataType),\n    (position, 1, Seq.empty, IntDataType),\n    (collectionId, 2, Seq.empty, IntDataType),\n    (term, 3, Seq.empty, StringDataType),\n    (packageName, 4, Seq.empty, StringDataType),\n    (cardType, 5, Seq.empty, StringDataType),\n    (intent, 6, Seq.empty, StringDataType),\n    (imagePath, 7, Seq.empty, StringDataType),\n    (notification, 11, Seq.empty, StringDataType)\n  )\n\n  prepareCursor[Card](0, cursorData)\n}\n\nclass CardRepositorySpec extends CardRepositorySpecification {\n\n  \"CardRepositoryClient component\" should {\n\n    \"addCard\" should {\n\n      \"return a Card object with a valid request\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) returns testCardId\n          val result = cardRepository\n            .addCard(collectionId = testCollectionId, data = createCardData)\n            .value\n            .run\n\n          result must beLike {\n            case Right(cardResult) =>\n              cardResult.id shouldEqual testCardId\n              cardResult.data.intent shouldEqual testIntent\n          }\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) throws contentResolverException\n          val result = cardRepository\n            .addCard(collectionId = testCollectionId, data = createCardData)\n            .value\n            .run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"addCards\" should {\n\n      \"return a sequence of addCard objects with a valid request\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.inserts(any, any, any, any) returns cardIdSeq\n          val result = cardRepository.addCards(datas = cardsWithCollectionIdSeq).value.run\n\n          result must beLike {\n            case Right(cards) =>\n              cards map (_.id) shouldEqual cardIdSeq\n              cards map (_.data.packageName) shouldEqual (cardDataSeq map (_.packageName))\n          }\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.inserts(any, any, any, any) throws contentResolverException\n          val result = cardRepository.addCards(datas = cardsWithCollectionIdSeq).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n\n    }\n\n    \"deleteCards\" should {\n\n      \"return a successful result when all the cards are deleted\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.delete(any, any, any, any) returns 1\n          val result = cardRepository.deleteCards().value.run\n          result shouldEqual Right(1)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.delete(any, any, any, any) throws contentResolverException\n          val result = cardRepository.deleteCards().value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"deleteCard\" should {\n\n      \"return a successful result when a valid cache category id is given\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.deleteById(any, any, any, any, any) returns 1\n          val result = cardRepository.deleteCard(testCollectionId, card.id).value.run\n          result shouldEqual Right(1)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper\n            .deleteById(any, any, any, any, any) throws contentResolverException\n          val result = cardRepository.deleteCard(testCollectionId, card.id).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"findCardById\" should {\n\n      \"return a Card object when a existent id is given\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.findById[CardEntity](any, any, any, any, any, any)(any) returns Some(\n            cardEntity)\n          val result = cardRepository.findCardById(id = testCardId).value.run\n\n          result must beLike {\n            case Right(maybeCard) =>\n              maybeCard must beSome[Card].which { card =>\n                card.id shouldEqual testCardId\n                card.data.intent shouldEqual testIntent\n              }\n          }\n        }\n\n      \"return None when a non-existent id is given\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.findById(any, any, any, any, any, any)(any) returns None\n          val result = cardRepository.findCardById(id = testNonExistingCardId).value.run\n          result shouldEqual Right(None)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.findById(any, any, any, any, any, any)(any) throws contentResolverException\n          val result = cardRepository.findCardById(id = testCardId).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"fetchCardsByCollection\" should {\n\n      \"return a Card sequence when a existent collection id is given\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$collectionId = ?\",\n            whereParams = Seq(testCollectionId.toString),\n            orderBy = s\"${CardEntity.position} asc\")(f = getListFromCursor(cardEntityFromCursor)) returns cardEntitySeq\n\n          val result =\n            cardRepository.fetchCardsByCollection(collectionId = testCollectionId).value.run\n          result shouldEqual Right(cardSeq)\n\n        }\n\n      \"fetchCardsByCollection should return an empty sequence when a non-existent collection id is given\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$collectionId = ?\",\n            whereParams = Seq(testNonExistingCollectionId.toString),\n            orderBy = s\"${CardEntity.position} asc\")(f = getListFromCursor(cardEntityFromCursor)) returns Seq.empty\n\n          val result = cardRepository\n            .fetchCardsByCollection(collectionId = testNonExistingCollectionId)\n            .value\n            .run\n          result shouldEqual Right(Seq.empty)\n\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$collectionId = ?\",\n            whereParams = Seq(testCollectionId.toString),\n            orderBy = s\"${CardEntity.position} asc\")(f = getListFromCursor(cardEntityFromCursor)) throws contentResolverException\n\n          val result =\n            cardRepository.fetchCardsByCollection(collectionId = testCollectionId).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"fetchCards\" should {\n\n      \"return all Cards\" in\n        new CardRepositoryScope {\n          contentResolverWrapper.fetchAll(uri = mockUri, projection = allFields)(\n            f = getListFromCursor(cardEntityFromCursor)) returns cardEntitySeq\n\n          val result = cardRepository.fetchCards.value.run\n          result shouldEqual Right(cardSeq)\n\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.fetchAll(uri = mockUri, projection = allFields)(\n            f = getListFromCursor(cardEntityFromCursor)) throws contentResolverException\n\n          val result = cardRepository.fetchCards.value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"fetchIterableCards\" should {\n\n      \"return an IterableCursor[Card] \" in\n        new CardMockCursor with CardRepositoryScope {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n\n          val result = cardRepository.fetchIterableCards(where = testMockWhere).value.run\n\n          result must beLike {\n            case Right(iterator) =>\n              toSeq(iterator) shouldEqual cardSeq\n          }\n\n          there was one(contentResolverWrapper)\n            .getCursor(mockUri, AppEntity.allFields, testMockWhere, Seq.empty, \"\")\n        }\n\n      \"return an a RepositoryException when a exception is thrown \" in\n        new CardMockCursor with CardRepositoryScope {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) throws contentResolverException\n\n          val result = cardRepository.fetchIterableCards(where = testMockWhere).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"updateCard\" should {\n\n      \"return a successful result when the card is updated\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.updateById(\n            uri = mockUri,\n            id = card.id,\n            values = createUpdateCardValues,\n            notificationUris = Seq(mockUri)) returns 1\n\n          val result = cardRepository.updateCard(card = card).value.run\n          result shouldEqual Right(1)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.updateById(\n            uri = mockUri,\n            id = card.id,\n            values = createUpdateCardValues,\n            notificationUris = Seq(mockUri)) throws contentResolverException\n\n          val result = cardRepository.updateCard(card = card).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"updateCards\" should {\n\n      \"return a successful result when the updateCard are updated\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.updateByIds(any, any, any, any) returns Seq(5)\n          val result = cardRepository.updateCards(cards = cardSeq).value.run\n          result shouldEqual Right(Seq(5))\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CardRepositoryScope {\n\n          contentResolverWrapper.updateByIds(any, any, any, any) throws contentResolverException\n          val result = cardRepository.updateCards(cards = cardSeq).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"getEntityFromCursor\" should {\n\n      \"return None when an empty cursor is given\" in\n        new EmptyCardMockCursor with CardRepositoryScope {\n\n          val result = getEntityFromCursor(cardEntityFromCursor)(mockCursor)\n\n          result must beNone\n        }\n\n      \"return a Card object when a cursor with data is given\" in\n        new CardMockCursor with CardRepositoryScope {\n\n          val result = getEntityFromCursor(cardEntityFromCursor)(mockCursor)\n\n          result must beSome[CardEntity].which { card =>\n            card.id shouldEqual cardEntity.id\n            card.data shouldEqual cardEntity.data\n          }\n        }\n    }\n\n    \"getListFromCursor\" should {\n\n      \"return an empty sequence when an empty cursor is given\" in\n        new EmptyCardMockCursor with CardRepositoryScope {\n\n          val result = getListFromCursor(cardEntityFromCursor)(mockCursor)\n\n          result should beEmpty\n        }\n\n      \"return a Card sequence when a cursor with data is given\" in\n        new CardMockCursor with CardRepositoryScope {\n\n          val result = getListFromCursor(cardEntityFromCursor)(mockCursor)\n\n          result shouldEqual cardEntitySeq\n        }\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/card/CardRepositoryTestData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.card\n\nimport cards.nine.repository.model.{Card, CardData, CardsWithCollectionId}\nimport cards.nine.repository.provider.{CardEntity, CardEntityData}\n\nimport scala.util.Random\n\ntrait CardRepositoryTestData {\n\n  val testCardId                  = Random.nextInt(10)\n  val testNonExistingCardId       = 15\n  val testPosition                = Random.nextInt(10)\n  val testCollectionId            = Random.nextInt(10)\n  val testNonExistingCollectionId = 15\n  val testTerm                    = Random.nextString(5)\n  val testPackageName             = Random.nextString(5)\n  val testType                    = Random.nextString(5)\n  val testIntent                  = Random.nextString(5)\n  val testImagePath               = Random.nextString(5)\n  val testStarRating              = Random.nextDouble()\n  val testNumDownloads            = Random.nextString(10)\n  val testNotification            = Random.nextString(10)\n  val testPackageNameOption       = Option(testPackageName)\n  val testImagePathOption         = Option(testImagePath)\n  val testNotificationOption      = Option(testNotification)\n  val testMockWhere               = \"mocked-where\"\n\n  val cardEntitySeq            = createCardEntitySeq(5)\n  val cardEntity               = cardEntitySeq(0)\n  val cardSeq                  = createCardSeq(5)\n  val cardDataSeq              = cardSeq map (_.data)\n  val cardsWithCollectionIdSeq = createCardWithCollectionIdSeq(5)\n  val cardIdSeq                = cardSeq map (_.id)\n  val card                     = cardSeq(0)\n\n  def createCardEntitySeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        CardEntity(\n          id = testCardId + i,\n          data = CardEntityData(\n            position = testPosition,\n            collectionId = testCollectionId,\n            term = testTerm,\n            packageName = testPackageName,\n            `type` = testType,\n            intent = testIntent,\n            imagePath = testImagePath,\n            notification = testNotification)))\n\n  def createCardSeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        Card(\n          id = testCardId + i,\n          data = CardData(\n            position = testPosition,\n            term = testTerm,\n            packageName = testPackageNameOption,\n            cardType = testType,\n            intent = testIntent,\n            imagePath = testImagePathOption,\n            notification = testNotificationOption)))\n\n  def createInsertCardValues =\n    Map[String, Any](\n      CardEntity.position     -> testPosition,\n      CardEntity.collectionId -> testCollectionId,\n      CardEntity.term         -> testTerm,\n      CardEntity.packageName  -> (testPackageNameOption orNull),\n      CardEntity.cardType     -> testType,\n      CardEntity.intent       -> testIntent,\n      CardEntity.imagePath    -> (testImagePathOption orNull),\n      CardEntity.notification -> (testNotificationOption orNull))\n\n  def createUpdateCardValues =\n    Map[String, Any](\n      CardEntity.position     -> testPosition,\n      CardEntity.term         -> testTerm,\n      CardEntity.packageName  -> (testPackageNameOption orNull),\n      CardEntity.cardType     -> testType,\n      CardEntity.intent       -> testIntent,\n      CardEntity.imagePath    -> (testImagePathOption orNull),\n      CardEntity.notification -> (testNotificationOption orNull))\n\n  def createCardData =\n    CardData(\n      position = testPosition,\n      term = testTerm,\n      packageName = testPackageNameOption,\n      cardType = testType,\n      intent = testIntent,\n      imagePath = testImagePathOption,\n      notification = testNotificationOption)\n\n  def createCardWithCollectionIdSeq(num: Int) = List.tabulate(num) { i =>\n    CardsWithCollectionId(collectionId = testCollectionId, data = cardDataSeq)\n  }\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/collection/CollectionRepositorySpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.collection\n\nimport android.net.Uri\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapperImpl, UriCreator}\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.repository.{IntDataType, MockCursor, StringDataType}\nimport cards.nine.models.IterableCursor._\nimport cards.nine.repository.RepositoryException\nimport cards.nine.repository.model.Collection\nimport cards.nine.repository.provider.CollectionEntity._\nimport cards.nine.repository.provider._\nimport cards.nine.repository.repositories._\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait CollectionRepositorySpecification\n    extends Specification\n    with DisjunctionMatchers\n    with Mockito {\n\n  trait CollectionRepositoryScope extends Scope with CollectionRepositoryTestData {\n\n    lazy val contentResolverWrapper = mock[ContentResolverWrapperImpl]\n\n    lazy val uriCreator = mock[UriCreator]\n\n    lazy val collectionRepository = new CollectionRepository(contentResolverWrapper, uriCreator)\n\n    lazy val mockUri = mock[Uri]\n\n    uriCreator.parse(any) returns mockUri\n\n    val contentResolverException = new RuntimeException(\"Irrelevant message\")\n\n  }\n\n}\n\ntrait CollectionMockCursor extends MockCursor with CollectionRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, collectionSeq map (_.id), IntDataType),\n    (position, 1, collectionSeq map (_.data.position), IntDataType),\n    (name, 2, collectionSeq map (_.data.name), StringDataType),\n    (collectionType, 3, collectionSeq map (_.data.collectionType), StringDataType),\n    (icon, 4, collectionSeq map (_.data.icon), StringDataType),\n    (themedColorIndex, 5, collectionSeq map (_.data.themedColorIndex), IntDataType),\n    (appsCategory, 6, collectionSeq map (_.data.appsCategory.orNull), StringDataType),\n    (originalSharedCollectionId,\n     8,\n     collectionSeq map (_.data.originalSharedCollectionId.orNull),\n     StringDataType),\n    (sharedCollectionId, 9, collectionSeq map (_.data.sharedCollectionId.orNull), StringDataType),\n    (sharedCollectionSubscribed,\n     10,\n     collectionSeq map (item =>\n                          if (item.data.sharedCollectionSubscribed getOrElse false) 1 else 0),\n     IntDataType)\n  )\n\n  prepareCursor[Collection](collectionSeq.size, cursorData)\n}\n\ntrait EmptyCollectionMockCursor extends MockCursor with CollectionRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, Seq.empty, IntDataType),\n    (position, 1, Seq.empty, IntDataType),\n    (name, 2, Seq.empty, StringDataType),\n    (collectionType, 3, Seq.empty, StringDataType),\n    (icon, 4, Seq.empty, StringDataType),\n    (themedColorIndex, 5, Seq.empty, IntDataType),\n    (appsCategory, 6, Seq.empty, StringDataType),\n    (originalSharedCollectionId, 8, Seq.empty, StringDataType),\n    (sharedCollectionId, 9, Seq.empty, StringDataType),\n    (sharedCollectionSubscribed, 10, Seq.empty, IntDataType)\n  )\n\n  prepareCursor[Collection](0, cursorData)\n}\n\nclass CollectionRepositorySpec extends CollectionRepositorySpecification {\n\n  \"CollectionRepositoryClient component\" should {\n\n    \"addCollection\" should {\n\n      \"return a Collection object with a valid request\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) returns testCollectionId\n          val result = collectionRepository.addCollection(data = createCollectionData).value.run\n\n          result must beLike {\n            case Right(c) =>\n              c.id shouldEqual testCollectionId\n              c.data.name shouldEqual testName\n          }\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) throws contentResolverException\n          val result = collectionRepository.addCollection(data = createCollectionData).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"addCollections\" should {\n\n      \"return a sequence of DockApp objects with a valid request\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.inserts(any, any, any, any) returns collectionIdSeq\n          val result = collectionRepository.addCollections(datas = collectionDataSeq).value.run\n\n          result must beLike {\n            case Right(collections) =>\n              collections map (_.id) shouldEqual collectionIdSeq\n              collections map (_.data.name) shouldEqual (collectionDataSeq map (_.name))\n          }\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.inserts(any, any, any, any) throws contentResolverException\n          val result = collectionRepository.addCollections(datas = collectionDataSeq).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n\n    }\n\n    \"deleteCollections\" should {\n\n      \"return a successful result when all collections are deleted\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.delete(any, any, any, any) returns 1\n          val result = collectionRepository.deleteCollections().value.run\n          result shouldEqual Right(1)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.delete(any, any, any, any) throws contentResolverException\n          val result = collectionRepository.deleteCollections().value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"deleteCollection\" should {\n\n      \"return a successful result when a valid collection id is given\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.deleteById(any, any, any, any, any) returns 1\n          val result = collectionRepository.deleteCollection(collection).value.run\n          result shouldEqual Right(1)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper\n            .deleteById(any, any, any, any, any) throws contentResolverException\n          val result = collectionRepository.deleteCollection(collection).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"findCollectionById\" should {\n\n      \"return a Collection object when a existing id is given\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.findById[CollectionEntity](any, any, any, any, any, any)(any) returns Some(\n            collectionEntity)\n          val result = collectionRepository.findCollectionById(id = testCollectionId).value.run\n\n          result must beLike {\n            case Right(maybeCollection) =>\n              maybeCollection must beSome[Collection].which { collection =>\n                collection.id shouldEqual testCollectionId\n                collection.data.name shouldEqual testName\n              }\n          }\n        }\n\n      \"return None when a non-existing id is given\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.findById(any, any, any, any, any, any)(any) returns None\n          val result =\n            collectionRepository.findCollectionById(id = testNonExistingCollectionId).value.run\n          result shouldEqual Right(None)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.findById(any, any, any, any, any, any)(any) throws contentResolverException\n          val result = collectionRepository.findCollectionById(id = testCollectionId).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"fetchCollectionBySharedCollectionId\" should {\n\n      \"return a Collection object when a existing shared collection id is given\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.fetch(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$sharedCollectionId = ?\",\n            whereParams = Seq(testSharedCollectionId),\n            orderBy = \"\")(f = getEntityFromCursor(collectionEntityFromCursor)) returns Some(\n            collectionEntity)\n\n          val result = collectionRepository\n            .fetchCollectionBySharedCollectionId(id = testSharedCollectionId)\n            .value\n            .run\n\n          result must beLike {\n            case Right(maybeCollection) =>\n              maybeCollection must beSome[Collection].which { collection =>\n                collection.id shouldEqual testCollectionId\n                collection.data.name shouldEqual testName\n              }\n          }\n        }\n\n      \"return None when a non-existing shared collection id is given\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.fetch(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$sharedCollectionId = ?\",\n            whereParams = Seq(testNonExistingSharedCollectionId),\n            orderBy = \"\")(f = getEntityFromCursor(collectionEntityFromCursor)) returns None\n\n          val result = collectionRepository\n            .fetchCollectionBySharedCollectionId(id = testNonExistingSharedCollectionId)\n            .value\n            .run\n          result shouldEqual Right(None)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new CollectionRepositoryScope {\n\n          contentResolverWrapper.fetch(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$sharedCollectionId = ?\",\n            whereParams = Seq(testSharedCollectionId),\n            orderBy = \"\")(f = getEntityFromCursor(collectionEntityFromCursor)) throws contentResolverException\n\n          val result = collectionRepository\n            .fetchCollectionBySharedCollectionId(id = testSharedCollectionId)\n            .value\n            .run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n\n      \"fetchCollectionsBySharedCollectionIds\" should {\n\n        \"return the Collections when some existing shared collection ids are given\" in\n          new CollectionRepositoryScope {\n\n            contentResolverWrapper.fetchAll[CollectionEntity](any, any, any, any, any)(any) returns Seq(\n              collectionEntity)\n            val result = collectionRepository\n              .fetchCollectionsBySharedCollectionIds(Seq(testSharedCollectionId))\n              .value\n              .run\n\n            result must beLike {\n              case Right(collections) =>\n                collections.headOption must beSome[Collection].which { collection =>\n                  collection.id shouldEqual testCollectionId\n                  collection.data.name shouldEqual testName\n                }\n            }\n          }\n\n        \"return a empty sequence when a non-existing shared collection id is given\" in\n          new CollectionRepositoryScope {\n\n            contentResolverWrapper.fetchAll[CollectionEntity](any, any, any, any, any)(any) returns Seq\n              .empty[CollectionEntity]\n\n            val result = collectionRepository\n              .fetchCollectionsBySharedCollectionIds(Seq(testNonExistingSharedCollectionId))\n              .value\n              .run\n            result shouldEqual Right(Seq.empty)\n          }\n\n        \"return a RepositoryException when a exception is thrown\" in\n          new CollectionRepositoryScope {\n\n            contentResolverWrapper.fetchAll[CollectionEntity](any, any, any, any, any)(any) throws contentResolverException\n\n            val result = collectionRepository\n              .fetchCollectionsBySharedCollectionIds(Seq(testSharedCollectionId))\n              .value\n              .run\n            result must beAnInstanceOf[Left[RepositoryException, _]]\n          }\n\n      }\n\n      \"fetchCollectionByPosition\" should {\n\n        \"return a Collection object when a existing position is given\" in\n          new CollectionRepositoryScope {\n\n            contentResolverWrapper.fetch(\n              uri = mockUri,\n              projection = allFields,\n              where = s\"$position = ?\",\n              whereParams = Seq(testPosition.toString),\n              orderBy = \"\")(f = getEntityFromCursor(collectionEntityFromCursor)) returns Some(\n              collectionEntity)\n            val result =\n              collectionRepository.fetchCollectionByPosition(position = testPosition).value.run\n\n            result must beLike {\n              case Right(maybeCollection) =>\n                maybeCollection must beSome[Collection].which { collection =>\n                  collection.id shouldEqual testCollectionId\n                  collection.data.position shouldEqual testPosition\n                }\n            }\n          }\n\n        \"return None when a non-existing position is given\" in\n          new CollectionRepositoryScope {\n\n            contentResolverWrapper.fetch(\n              uri = mockUri,\n              projection = allFields,\n              where = s\"$position = ?\",\n              whereParams = Seq(testNonExistingPosition.toString),\n              orderBy = \"\")(f = getEntityFromCursor(collectionEntityFromCursor)) returns None\n            val result = collectionRepository\n              .fetchCollectionByPosition(position = testNonExistingPosition)\n              .value\n              .run\n            result shouldEqual Right(None)\n          }\n\n        \"return a RepositoryException when a exception is thrown\" in\n          new CollectionRepositoryScope {\n\n            contentResolverWrapper.fetch(\n              uri = mockUri,\n              projection = allFields,\n              where = s\"$position = ?\",\n              whereParams = Seq(testPosition.toString),\n              orderBy = \"\")(f = getEntityFromCursor(collectionEntityFromCursor)) throws contentResolverException\n\n            val result =\n              collectionRepository.fetchCollectionByPosition(position = testPosition).value.run\n            result must beAnInstanceOf[Left[RepositoryException, _]]\n          }\n      }\n\n      \"fetchIterableCollections\" should {\n\n        \"return an IterableCursor of Collection \" in\n          new CollectionMockCursor with CollectionRepositoryScope {\n\n            contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n\n            val result =\n              collectionRepository.fetchIterableCollections(where = testMockWhere).value.run\n\n            result must beLike {\n              case Right(iterator) =>\n                toSeq(iterator) shouldEqual collectionSeq\n            }\n\n            there was one(contentResolverWrapper)\n              .getCursor(mockUri, AppEntity.allFields, testMockWhere, Seq.empty, \"\")\n          }\n\n        \"return an a RepositoryException when a exception is thrown \" in\n          new CollectionMockCursor with CollectionRepositoryScope {\n\n            contentResolverWrapper\n              .getCursor(any, any, any, any, any) throws contentResolverException\n\n            val result =\n              collectionRepository.fetchIterableCollections(where = testMockWhere).value.run\n            result must beAnInstanceOf[Left[RepositoryException, _]]\n          }\n      }\n\n      \"fetchSortedCollections\" should {\n\n        \"return all the cache categories stored in the database\" in\n          new CollectionRepositoryScope {\n\n            contentResolverWrapper.fetchAll(\n              uri = mockUri,\n              projection = allFields,\n              where = \"\",\n              whereParams = Seq.empty,\n              orderBy = s\"$position asc\")(f = getListFromCursor(collectionEntityFromCursor)) returns collectionEntitySeq\n\n            val result = collectionRepository.fetchSortedCollections.value.run\n            result shouldEqual Right(collectionSeq)\n          }\n\n        \"return a RepositoryException when a exception is thrown\" in\n          new CollectionRepositoryScope {\n\n            contentResolverWrapper.fetchAll(\n              uri = mockUri,\n              projection = allFields,\n              where = \"\",\n              whereParams = Seq.empty,\n              orderBy = s\"$position asc\")(f = getListFromCursor(collectionEntityFromCursor)) throws contentResolverException\n\n            val result = collectionRepository.fetchSortedCollections.value.run\n            result must beAnInstanceOf[Left[RepositoryException, _]]\n          }\n      }\n\n      \"updateCollection\" should {\n\n        \"return a successful result when the collection is updated\" in\n          new CollectionRepositoryScope {\n\n            contentResolverWrapper.updateById(any, any, any, any) returns 1\n            val result = collectionRepository.updateCollection(collection = collection).value.run\n            result shouldEqual Right(1)\n          }\n\n        \"return a RepositoryException when a exception is thrown\" in\n          new CollectionRepositoryScope {\n\n            contentResolverWrapper.updateById(any, any, any, any) throws contentResolverException\n            val result = collectionRepository.updateCollection(collection = collection).value.run\n            result must beAnInstanceOf[Left[RepositoryException, _]]\n          }\n      }\n\n      \"updateCollections\" should {\n\n        \"return a successful result when the updateCollection are updated\" in\n          new CollectionRepositoryScope {\n\n            contentResolverWrapper.updateByIds(any, any, any, any) returns Seq(5)\n            val result =\n              collectionRepository.updateCollections(collections = collectionSeq).value.run\n            result shouldEqual Right(Seq(5))\n          }\n\n        \"return a RepositoryException when a exception is thrown\" in\n          new CollectionRepositoryScope {\n\n            contentResolverWrapper.updateByIds(any, any, any, any) throws contentResolverException\n            val result =\n              collectionRepository.updateCollections(collections = collectionSeq).value.run\n            result must beAnInstanceOf[Left[RepositoryException, _]]\n          }\n      }\n\n      \"getEntityFromCursor\" should {\n\n        \"return None when an empty cursor is given\" in\n          new EmptyCollectionMockCursor with Scope {\n\n            val result = getEntityFromCursor(collectionEntityFromCursor)(mockCursor)\n            result must beNone\n          }\n\n        \"return a Collection object when a cursor with data is given\" in\n          new CollectionMockCursor with Scope {\n\n            val result = getEntityFromCursor(collectionEntityFromCursor)(mockCursor)\n\n            result must beSome[CollectionEntity].which { collection =>\n              collection.id shouldEqual collectionEntity.id\n              collection.data shouldEqual collectionEntity.data\n            }\n          }\n      }\n\n      \"getListFromCursor\" should {\n\n        \"return an empty sequence when an empty cursor is given\" in\n          new EmptyCollectionMockCursor with Scope {\n\n            val result = getListFromCursor(collectionEntityFromCursor)(mockCursor)\n            result should beEmpty\n          }\n\n        \"return a Collection sequence when a cursor with data is given\" in\n          new CollectionMockCursor with Scope {\n\n            val result = getListFromCursor(collectionEntityFromCursor)(mockCursor)\n            result shouldEqual collectionEntitySeq\n          }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/collection/CollectionRepositoryTestData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.collection\n\nimport cards.nine.repository.model.{Collection, CollectionData}\nimport cards.nine.repository.provider.{CollectionEntity, CollectionEntityData}\n\nimport scala.util.Random\n\ntrait CollectionRepositoryTestData {\n\n  val testCollectionId                     = Random.nextInt(10)\n  val testNonExistingCollectionId          = 15\n  val testPosition                         = Random.nextInt(10)\n  val testNonExistingPosition              = 15\n  val testName                             = Random.nextString(5)\n  val testCollectionType                   = Random.nextString(5)\n  val testIcon                             = Random.nextString(5)\n  val testThemedColorIndex                 = Random.nextInt(10)\n  val testAppsCategory                     = Random.nextString(5)\n  val testOriginalSharedCollectionId       = Random.nextString(5)\n  val testSharedCollectionId               = Random.nextString(5)\n  val testNonExistingSharedCollectionId    = Random.nextString(5)\n  val testSharedCollectionSubscribed       = Random.nextInt(10) < 5\n  val testAppsCategoryOption               = Option(testAppsCategory)\n  val testOriginalSharedCollectionIdOption = Option(testOriginalSharedCollectionId)\n  val testSharedCollectionIdOption         = Option(testSharedCollectionId)\n  val testSharedCollectionSubscribedOption = Option(testSharedCollectionSubscribed)\n  val testMockWhere                        = \"mock-where\"\n\n  val collectionEntitySeq = createCollectionEntitySeq(5)\n  val collectionEntity    = collectionEntitySeq(0)\n  val collectionSeq       = createCollectionSeq(5)\n  val collectionIdSeq     = collectionSeq map (_.id)\n  val collectionDataSeq   = collectionSeq map (_.data)\n  val collection          = collectionSeq(0)\n\n  def createCollectionEntitySeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        CollectionEntity(\n          id = testCollectionId + i,\n          data = CollectionEntityData(\n            position = testPosition,\n            name = testName,\n            `type` = testCollectionType,\n            icon = testIcon,\n            themedColorIndex = testThemedColorIndex,\n            appsCategory = testAppsCategory,\n            originalSharedCollectionId = testOriginalSharedCollectionId,\n            sharedCollectionId = testSharedCollectionId,\n            sharedCollectionSubscribed = testSharedCollectionSubscribed)))\n\n  def createCollectionSeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        Collection(\n          id = testCollectionId + i,\n          data = CollectionData(\n            position = testPosition,\n            name = testName,\n            collectionType = testCollectionType,\n            icon = testIcon,\n            themedColorIndex = testThemedColorIndex,\n            appsCategory = testAppsCategoryOption,\n            originalSharedCollectionId = testOriginalSharedCollectionIdOption,\n            sharedCollectionId = testSharedCollectionIdOption,\n            sharedCollectionSubscribed = testSharedCollectionSubscribedOption)))\n\n  def createCollectionValues =\n    Map[String, Any](\n      CollectionEntity.position                   -> testPosition,\n      CollectionEntity.name                       -> testName,\n      CollectionEntity.collectionType             -> testCollectionType,\n      CollectionEntity.icon                       -> testIcon,\n      CollectionEntity.themedColorIndex           -> testThemedColorIndex,\n      CollectionEntity.appsCategory               -> (testAppsCategoryOption orNull),\n      CollectionEntity.originalSharedCollectionId -> (testOriginalSharedCollectionIdOption orNull),\n      CollectionEntity.sharedCollectionId         -> (testSharedCollectionIdOption orNull),\n      CollectionEntity.sharedCollectionSubscribed -> (testSharedCollectionSubscribedOption getOrElse false))\n\n  def createCollectionData =\n    CollectionData(\n      position = testPosition,\n      name = testName,\n      collectionType = testCollectionType,\n      icon = testIcon,\n      themedColorIndex = testThemedColorIndex,\n      appsCategory = testAppsCategoryOption,\n      originalSharedCollectionId = testOriginalSharedCollectionIdOption,\n      sharedCollectionId = testSharedCollectionIdOption,\n      sharedCollectionSubscribed = testSharedCollectionSubscribedOption)\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/dockapp/DockAppRepositorySpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.dockapp\n\nimport android.net.Uri\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapperImpl, UriCreator}\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.repository.{IntDataType, MockCursor, StringDataType}\nimport cards.nine.repository.RepositoryException\nimport cards.nine.repository.model.DockApp\nimport cards.nine.repository.provider.DockAppEntity._\nimport cards.nine.repository.provider._\nimport cards.nine.repository.repositories.DockAppRepository\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait DockAppRepositorySpecification extends Specification with DisjunctionMatchers with Mockito {\n\n  trait DockAppRepositoryScope extends Scope with DockAppRepositoryTestData {\n\n    lazy val contentResolverWrapper = mock[ContentResolverWrapperImpl]\n\n    lazy val uriCreator = mock[UriCreator]\n\n    lazy val dockAppRepository = new DockAppRepository(contentResolverWrapper, uriCreator)\n\n    lazy val mockUri = mock[Uri]\n\n    uriCreator.parse(any) returns mockUri\n\n    val contentResolverException = new RuntimeException(\"Irrelevant message\")\n  }\n\n}\n\ntrait DockAppMockCursor extends MockCursor with DockAppRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, dockAppSeq map (_.id), IntDataType),\n    (name, 1, dockAppSeq map (_.data.name), StringDataType),\n    (dockType, 2, dockAppSeq map (_.data.dockType), StringDataType),\n    (intent, 4, dockAppSeq map (_.data.intent), StringDataType),\n    (imagePath, 5, dockAppSeq map (_.data.imagePath), StringDataType),\n    (position, 6, dockAppSeq map (_.data.position), IntDataType))\n\n  prepareCursor[DockApp](dockAppSeq.size, cursorData)\n}\n\ntrait EmptyDockAppMockCursor extends MockCursor with DockAppRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, Seq.empty, IntDataType),\n    (name, 1, Seq.empty, StringDataType),\n    (dockType, 2, Seq.empty, StringDataType),\n    (intent, 4, Seq.empty, StringDataType),\n    (imagePath, 5, Seq.empty, StringDataType),\n    (position, 6, Seq.empty, IntDataType))\n\n  prepareCursor[DockApp](0, cursorData)\n}\n\nclass DockAppRepositorySpec extends DockAppRepositorySpecification {\n\n  \"DockAppRepositoryClient component\" should {\n\n    \"addDockApp\" should {\n\n      \"return a DockApp object with a valid request\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) returns testId\n          val result = dockAppRepository.addDockApp(data = createDockAppData).value.run\n\n          result must beLike {\n            case Right(dockAppResult) =>\n              dockAppResult.id shouldEqual testId\n              dockAppResult.data.name shouldEqual testName\n          }\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) throws contentResolverException\n          val result = dockAppRepository.addDockApp(data = createDockAppData).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"addDockApps\" should {\n\n      \"return a sequence of DockApp objects with a valid request\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.inserts(any, any, any, any) returns dockAppIdSeq\n          val result = dockAppRepository.addDockApps(datas = dockAppDataSeq).value.run\n\n          result must beLike {\n            case Right(dockApps) =>\n              dockApps map (_.id) shouldEqual dockAppIdSeq\n              dockApps map (_.data.name) shouldEqual (dockAppDataSeq map (_.name))\n          }\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.inserts(any, any, any, any) throws contentResolverException\n          val result = dockAppRepository.addDockApps(datas = dockAppDataSeq).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n\n    }\n\n    \"deleteDockApps\" should {\n\n      \"return a successful result when all the dockApps are deleted\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.delete(any, any, any, any) returns 1\n          val result = dockAppRepository.deleteDockApps().value.run\n          result shouldEqual Right(1)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.delete(any, any, any, any) throws contentResolverException\n          val result = dockAppRepository.deleteDockApps().value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"deleteDockApp\" should {\n\n      \"return a successful result when a valid dockApp id is given\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.deleteById(any, any, any, any, any) returns 1\n          val result = dockAppRepository.deleteDockApp(dockApp).value.run\n          result shouldEqual Right(1)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper\n            .deleteById(any, any, any, any, any) throws contentResolverException\n          val result = dockAppRepository.deleteDockApp(dockApp).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"findDockAppById\" should {\n\n      \"return a DockApp object when a existing id is given\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.findById[DockAppEntity](any, any, any, any, any, any)(any) returns Some(\n            dockAppEntity)\n          val result = dockAppRepository.findDockAppById(id = testId).value.run\n\n          result must beLike {\n            case Right(maybeDockApp) =>\n              maybeDockApp must beSome[DockApp].which { dockApp =>\n                dockApp.id shouldEqual testId\n                dockApp.data.name shouldEqual testName\n              }\n          }\n        }\n\n      \"return None when a non-existing id is given\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.findById(any, any, any, any, any, any)(any) returns None\n          val result = dockAppRepository.findDockAppById(id = testNonExistingId).value.run\n          result shouldEqual Right(None)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.findById(any, any, any, any, any, any)(any) throws contentResolverException\n          val result = dockAppRepository.findDockAppById(id = testId).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"updateDockApp\" should {\n\n      \"return a successful result when the dockApp is updated\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.updateById(any, any, any, any) returns 1\n          val result = dockAppRepository.updateDockApp(item = dockApp).value.run\n          result shouldEqual Right(1)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.updateById(any, any, any, any) throws contentResolverException\n          val result = dockAppRepository.updateDockApp(item = dockApp).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"updateDockApps\" should {\n\n      \"return a successful result when the dockApps are updated\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.updateByIds(any, any, any, any) returns Seq(5)\n          val result = dockAppRepository.updateDockApps(items = dockAppSeq).value.run\n          result shouldEqual Right(Seq(5))\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.updateByIds(any, any, any, any) throws contentResolverException\n          val result = dockAppRepository.updateDockApps(items = dockAppSeq).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"fetchDockApps\" should {\n\n      \"return all DockApps\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = \"\",\n            whereParams = Seq.empty,\n            orderBy = s\"${DockAppEntity.position} asc\")(\n            f = getListFromCursor(dockAppEntityFromCursor)) returns dockAppEntitySeq\n\n          val result = dockAppRepository.fetchDockApps().value.run\n          result shouldEqual Right(dockAppSeq)\n        }\n\n      \"return all DockApps that match the where clause\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$position = ?\",\n            whereParams = Seq(testPosition.toString),\n            orderBy = s\"${DockAppEntity.position} asc\")(\n            f = getListFromCursor(dockAppEntityFromCursor)) returns dockAppEntitySeq\n\n          val result = dockAppRepository\n            .fetchDockApps(where = s\"$position = ?\", whereParams = Seq(testPosition.toString))\n            .value\n            .run\n          result shouldEqual Right(dockAppSeq)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new DockAppRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = \"\",\n            whereParams = Seq.empty,\n            orderBy = s\"${DockAppEntity.position} asc\")(\n            f = getListFromCursor(dockAppEntityFromCursor)) throws contentResolverException\n\n          val result = dockAppRepository.fetchDockApps().value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"getEntityFromCursor\" should {\n\n      \"return None when an empty cursor is given\" in\n        new EmptyDockAppMockCursor with Scope {\n\n          val result = getEntityFromCursor(dockAppEntityFromCursor)(mockCursor)\n          result must beNone\n        }\n\n      \"return a DockApp object when a cursor with data is given\" in\n        new DockAppMockCursor with Scope {\n\n          val result = getEntityFromCursor(dockAppEntityFromCursor)(mockCursor)\n\n          result must beSome[DockAppEntity].which { dockApp =>\n            dockApp.id shouldEqual dockAppEntity.id\n            dockApp.data shouldEqual dockAppEntity.data\n          }\n        }\n    }\n\n    \"getListFromCursor\" should {\n\n      \"return an empty sequence when an empty cursor is given\" in\n        new EmptyDockAppMockCursor with Scope {\n\n          val result = getListFromCursor(dockAppEntityFromCursor)(mockCursor)\n          result should beEmpty\n        }\n\n      \"return a DockApp sequence when a cursor with data is given\" in\n        new DockAppMockCursor with Scope {\n\n          val result = getListFromCursor(dockAppEntityFromCursor)(mockCursor)\n          result shouldEqual dockAppEntitySeq\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/dockapp/DockAppRepositoryTestData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.dockapp\n\nimport cards.nine.repository.model.{DockApp, DockAppData}\nimport cards.nine.repository.provider.{DockAppEntity, DockAppEntityData}\n\nimport scala.util.Random\n\ntrait DockAppRepositoryTestData {\n\n  val testId            = Random.nextInt(10)\n  val testNonExistingId = 15\n  val testName          = Random.nextString(10)\n  val testCardType      = Random.nextString(10)\n  val testIntent        = Random.nextString(10)\n  val testImagePath     = Random.nextString(10)\n  val testPosition      = Random.nextInt(5)\n  val testMockWhere     = \"mock-where\"\n\n  val dockAppEntitySeq = createDockAppEntitySeq(5)\n  val dockAppEntity    = dockAppEntitySeq(0)\n  val dockAppSeq       = createDockAppSeq(5)\n  val dockAppIdSeq     = dockAppSeq map (_.id)\n  val dockAppDataSeq   = dockAppSeq map (_.data)\n  val dockApp          = dockAppSeq(0)\n\n  def createDockAppEntitySeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        DockAppEntity(\n          id = testId + i,\n          data = DockAppEntityData(\n            name = testName,\n            dockType = testCardType,\n            intent = testIntent,\n            imagePath = testImagePath,\n            position = testPosition)))\n\n  def createDockAppSeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        DockApp(\n          id = testId + i,\n          data = DockAppData(\n            name = testName,\n            dockType = testCardType,\n            intent = testIntent,\n            imagePath = testImagePath,\n            position = testPosition)))\n\n  def createDockAppValues =\n    Map[String, Any](\n      DockAppEntity.name      -> testName,\n      DockAppEntity.dockType  -> testCardType,\n      DockAppEntity.intent    -> testIntent,\n      DockAppEntity.imagePath -> testImagePath,\n      DockAppEntity.position  -> testPosition)\n\n  def createDockAppData =\n    DockAppData(\n      name = testName,\n      dockType = testCardType,\n      intent = testIntent,\n      imagePath = testImagePath,\n      position = testPosition)\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/moment/MomentRepositorySpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.moment\n\nimport android.net.Uri\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapperImpl, UriCreator}\nimport cards.nine.commons.javaNull\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.repository.{IntDataType, MockCursor, StringDataType}\nimport cards.nine.models.IterableCursor._\nimport cards.nine.repository.RepositoryException\nimport cards.nine.repository.model.Moment\nimport cards.nine.repository.provider.MomentEntity._\nimport cards.nine.repository.provider._\nimport cards.nine.repository.repositories.MomentRepository\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait MomentRepositorySpecification extends Specification with DisjunctionMatchers with Mockito {\n\n  trait MomentRepositoryScope extends Scope with MomentRepositoryTestData {\n\n    lazy val contentResolverWrapper = mock[ContentResolverWrapperImpl]\n\n    lazy val uriCreator = mock[UriCreator]\n\n    lazy val momentRepository = new MomentRepository(contentResolverWrapper, uriCreator)\n\n    lazy val mockUri = mock[Uri]\n\n    uriCreator.parse(any) returns mockUri\n\n    val contentResolverException = new RuntimeException(\"Irrelevant message\")\n  }\n\n}\n\ntrait MomentMockCursor extends MockCursor with MomentRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, momentSeq map (_.id), IntDataType),\n    (collectionId, 1, momentSeq map (_.data.collectionId getOrElse javaNull), IntDataType),\n    (timeslot, 2, momentSeq map (_.data.timeslot), StringDataType),\n    (wifi, 4, momentSeq map (_.data.wifi), StringDataType),\n    (headphone, 5, momentSeq map (item => if (item.data.headphone) 1 else 0), IntDataType),\n    (momentType, 6, momentSeq map (_.data.momentType getOrElse javaNull), StringDataType),\n    (bluetooth, 7, momentSeq map (_.data.bluetooth), StringDataType))\n\n  prepareCursor[Moment](momentSeq.size, cursorData)\n}\n\ntrait EmptyMomentMockCursor extends MockCursor with MomentRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, Seq.empty, IntDataType),\n    (collectionId, 1, Seq.empty, IntDataType),\n    (timeslot, 2, Seq.empty, StringDataType),\n    (wifi, 4, Seq.empty, StringDataType),\n    (headphone, 5, Seq.empty, IntDataType),\n    (momentType, 6, Seq.empty, StringDataType),\n    (bluetooth, 7, Seq.empty, StringDataType))\n\n  prepareCursor[Moment](0, cursorData)\n}\n\nclass MomentRepositorySpec extends MomentRepositorySpecification {\n\n  \"MomentRepositoryClient component\" should {\n\n    \"addMoment\" should {\n\n      \"return a Moment object with a valid request\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) returns testId\n          val result = momentRepository.addMoment(data = createMomentData).value.run\n\n          result must beLike {\n            case Right(momentResult) =>\n              momentResult.id shouldEqual testId\n              momentResult.data.collectionId shouldEqual testCollectionIdOption\n          }\n        }\n\n      \"return a Moment object with a collectionId = None with a valid request with a collectionId = None\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) returns testId\n          val result = momentRepository.addMoment(data = createMomentDataCollection).value.run\n\n          result must beLike {\n            case Right(momentResult) =>\n              momentResult.id shouldEqual testId\n              momentResult.data.collectionId shouldEqual None\n          }\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) throws contentResolverException\n          val result = momentRepository.addMoment(data = createMomentData).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"addMoments\" should {\n\n      \"return a sequence of Moment objects with a valid request\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.inserts(any, any, any, any) returns momentIdSeq\n\n          val result = momentRepository.addMoments(datas = momentDataSeq).value.run\n\n          result must beLike {\n            case Right(moments) =>\n              moments map (_.data.wifi) shouldEqual (momentSeq map (_.data.wifi))\n              moments map (_.id) shouldEqual momentIdSeq\n          }\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.inserts(any, any, any, any) throws contentResolverException\n          val result = momentRepository.addMoments(datas = momentDataSeq).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"deleteMoments\" should {\n\n      \"return a successful result when all the moments are deleted\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.delete(any, any, any, any) returns 1\n          val result = momentRepository.deleteMoments().value.run\n          result shouldEqual Right(1)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.delete(any, any, any, any) throws contentResolverException\n          val result = momentRepository.deleteMoments().value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"deleteMoment\" should {\n\n      \"return a successful result when a valid moment id is given\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.deleteById(any, any, any, any, any) returns 1\n          val result = momentRepository.deleteMoment(moment.id).value.run\n          result shouldEqual Right(1)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper\n            .deleteById(any, any, any, any, any) throws contentResolverException\n          val result = momentRepository.deleteMoment(moment.id).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"findMomentById\" should {\n\n      \"return a Moment object when a existing id is given\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.findById(uri = mockUri, id = testId, projection = allFields)(\n            f = getEntityFromCursor(momentEntityFromCursor)) returns Some(momentEntity)\n\n          val result = momentRepository.findMomentById(id = testId).value.run\n\n          result must beLike {\n            case Right(maybeMoment) =>\n              maybeMoment must beSome[Moment].which { moment =>\n                moment.id shouldEqual testId\n                moment.data.collectionId shouldEqual testCollectionIdOption\n              }\n          }\n        }\n\n      \"return None when a non-existing id is given\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.findById(any, any, any, any, any, any)(any) returns None\n          val result = momentRepository.findMomentById(id = testNonExistingId).value.run\n          result shouldEqual Right(None)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.findById(any, any, any, any, any, any)(any) throws contentResolverException\n          val result = momentRepository.findMomentById(id = testId).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"fetchMomentByCollectionId\" should {\n\n      \"return a Moment object when a existing collectionId is given\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.fetch(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$collectionId = ?\",\n            whereParams = Seq(testCollectionId.toString),\n            orderBy = \"\")(f = getEntityFromCursor(momentEntityFromCursor)) returns Some(\n            momentEntity)\n          val result =\n            momentRepository.fetchMomentByCollectionId(collectionId = testCollectionId).value.run\n\n          result must beLike {\n            case Right(maybeMoment) =>\n              maybeMoment must beSome[Moment].which { moment =>\n                moment.id shouldEqual testId\n                moment.data.collectionId shouldEqual testCollectionIdOption\n              }\n          }\n        }\n\n      \"return None when a non-existing collectionId is given\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.fetch(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$collectionId = ?\",\n            whereParams = Seq(testNonExistingCollectionId.toString),\n            orderBy = \"\")(f = getEntityFromCursor(momentEntityFromCursor)) returns None\n          val result = momentRepository\n            .fetchMomentByCollectionId(collectionId = testNonExistingCollectionId)\n            .value\n            .run\n          result shouldEqual Right(None)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.fetch(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$collectionId = ?\",\n            whereParams = Seq(testCollectionId.toString),\n            orderBy = \"\")(f = getEntityFromCursor(momentEntityFromCursor)) throws contentResolverException\n\n          val result =\n            momentRepository.fetchMomentByCollectionId(collectionId = testCollectionId).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"updateMoment\" should {\n\n      \"return a successful result when the moment is updated\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.updateById(any, any, any, any) returns 1\n          val result = momentRepository.updateMoment(item = moment).value.run\n          result shouldEqual Right(1)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.updateById(any, any, any, any) throws contentResolverException\n          val result = momentRepository.updateMoment(item = moment).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"fetchMoments\" should {\n\n      \"return all Moments\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = \"\",\n            whereParams = Seq.empty,\n            orderBy = \"\")(f = getListFromCursor(momentEntityFromCursor)) returns momentEntitySeq\n\n          val result = momentRepository.fetchMoments().value.run\n          result shouldEqual Right(momentSeq)\n        }\n\n      \"return all Moments that match the where clause\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$wifi = ?\",\n            whereParams = Seq(testWifi.toString),\n            orderBy = \"\")(f = getListFromCursor(momentEntityFromCursor)) returns momentEntitySeq\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$wifi = ?\",\n            whereParams = Seq(testWifi.toString))(f = getListFromCursor(momentEntityFromCursor)) returns momentEntitySeq\n\n          val result = momentRepository\n            .fetchMoments(where = s\"$wifi = ?\", whereParams = Seq(testWifi.toString))\n            .value\n            .run\n          result shouldEqual Right(momentSeq)\n\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new MomentRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = \"\",\n            whereParams = Seq.empty,\n            orderBy = \"\")(f = getListFromCursor(momentEntityFromCursor)) throws contentResolverException\n\n          val result = momentRepository.fetchMoments().value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"fetchIterableMoments\" should {\n\n      \"return an IterableCursor of Moment\" in\n        new MomentMockCursor with MomentRepositoryScope {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n\n          val result = momentRepository.fetchIterableMoments(where = testMockWhere).value.run\n\n          result must beLike {\n            case Right(iterator) =>\n              toSeq(iterator) shouldEqual momentSeq\n          }\n\n          there was one(contentResolverWrapper)\n            .getCursor(mockUri, AppEntity.allFields, testMockWhere, Seq.empty, \"\")\n        }\n\n      \"return an a RepositoryException when a exception is thrown \" in\n        new MomentMockCursor with MomentRepositoryScope {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) throws contentResolverException\n\n          val result = momentRepository.fetchIterableMoments(where = testMockWhere).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"getEntityFromCursor\" should {\n\n      \"return None when an empty cursor is given\" in\n        new EmptyMomentMockCursor with Scope {\n\n          val result = getEntityFromCursor(momentEntityFromCursor)(mockCursor)\n          result must beNone\n        }\n\n      \"return a Moment object when a cursor with data is given\" in\n        new MomentMockCursor with Scope {\n\n          val result = getEntityFromCursor(momentEntityFromCursor)(mockCursor)\n\n          result must beSome[MomentEntity].which { moment =>\n            moment.id shouldEqual momentEntity.id\n            moment.data shouldEqual momentEntity.data\n          }\n        }\n    }\n\n    \"getListFromCursor\" should {\n\n      \"return an empty sequence when an empty cursor is given\" in\n        new EmptyMomentMockCursor with Scope {\n\n          val result = getListFromCursor(momentEntityFromCursor)(mockCursor)\n          result should beEmpty\n        }\n\n      \"return a Moment sequence when a cursor with data is given\" in\n        new MomentMockCursor with Scope {\n\n          val result = getListFromCursor(momentEntityFromCursor)(mockCursor)\n          result shouldEqual momentEntitySeq\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/moment/MomentRepositoryTestData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.moment\n\nimport cards.nine.repository.model.{Moment, MomentData}\nimport cards.nine.repository.provider.{MomentEntity, MomentEntityData}\n\nimport scala.util.Random\n\ntrait MomentRepositoryTestData {\n\n  val testId                      = Random.nextInt(10)\n  val testNonExistingId           = 15\n  val testCollectionId            = Random.nextInt(5)\n  val testNonExistingCollectionId = Random.nextInt(5) + 100\n  val testTimeslot                = Random.nextString(10)\n  val testWifi                    = Random.nextString(10)\n  val testBluetooth               = Random.nextString(10)\n  val testHeadphone               = Random.nextBoolean()\n  val testMomentType              = Random.nextString(10)\n  val testCollectionIdOption      = Option(testCollectionId)\n  val testMockWhere               = \"mock-where\"\n\n  val momentEntitySeq = createMomentEntitySeq(5)\n  val momentEntity    = momentEntitySeq(0)\n  val momentSeq       = createMomentSeq(5)\n  val momentIdSeq     = momentSeq map (_.id)\n  val momentDataSeq   = momentSeq map (_.data)\n  val moment          = momentSeq(0)\n\n  def createMomentEntitySeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        MomentEntity(\n          id = testId + i,\n          data = MomentEntityData(\n            collectionId = Some(testCollectionId),\n            timeslot = testTimeslot,\n            wifi = testWifi,\n            bluetooth = testBluetooth,\n            headphone = testHeadphone,\n            momentType = testMomentType)))\n\n  def createMomentSeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        Moment(\n          id = testId + i,\n          data = MomentData(\n            collectionId = testCollectionIdOption,\n            timeslot = testTimeslot,\n            wifi = testWifi,\n            bluetooth = testBluetooth,\n            headphone = testHeadphone,\n            momentType = Option(testMomentType))))\n\n  def createMomentValues =\n    Map[String, Any](\n      MomentEntity.collectionId -> (testCollectionIdOption orNull),\n      MomentEntity.timeslot     -> testTimeslot,\n      MomentEntity.wifi         -> testWifi,\n      MomentEntity.headphone    -> testHeadphone,\n      MomentEntity.momentType   -> testMomentType)\n\n  def createMomentData =\n    MomentData(\n      collectionId = testCollectionIdOption,\n      timeslot = testTimeslot,\n      wifi = testWifi,\n      bluetooth = testBluetooth,\n      headphone = testHeadphone,\n      momentType = Option(testMomentType))\n\n  def createMomentValuesCollection =\n    Map[String, Any](\n      MomentEntity.collectionId -> (None orNull),\n      MomentEntity.timeslot     -> testTimeslot,\n      MomentEntity.wifi         -> testWifi,\n      MomentEntity.bluetooth    -> testBluetooth,\n      MomentEntity.headphone    -> testHeadphone,\n      MomentEntity.momentType   -> testMomentType)\n\n  def createMomentDataCollection =\n    MomentData(\n      collectionId = None,\n      timeslot = testTimeslot,\n      wifi = testWifi,\n      bluetooth = testBluetooth,\n      headphone = testHeadphone,\n      momentType = Option(testMomentType))\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/user/UserRepositorySpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.user\n\nimport android.net.Uri\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapperImpl, UriCreator}\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.repository.{IntDataType, MockCursor, StringDataType}\nimport cards.nine.models.IterableCursor\nimport cards.nine.models.IterableCursor._\nimport cards.nine.repository.RepositoryException\nimport cards.nine.repository.model.User\nimport cards.nine.repository.provider.UserEntity._\nimport cards.nine.repository.provider._\nimport cards.nine.repository.repositories.UserRepository\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\nimport scala.language.postfixOps\n\ntrait UserRepositorySpecification extends Specification with DisjunctionMatchers with Mockito {\n\n  val contentResolverException = new RuntimeException(\"Irrelevant message\")\n\n  trait UserRepositoryScope extends Scope {\n\n    lazy val contentResolverWrapper = mock[ContentResolverWrapperImpl]\n\n    lazy val uriCreator = mock[UriCreator]\n\n    lazy val userRepository = new UserRepository(contentResolverWrapper, uriCreator)\n\n    lazy val mockUri = mock[Uri]\n\n    uriCreator.parse(any) returns mockUri\n  }\n\n}\n\ntrait UserMockCursor extends MockCursor with UserRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, userSeq map (_.id), IntDataType),\n    (email, 1, userSeq map (_.data.email orNull), StringDataType),\n    (apiKey, 2, userSeq map (_.data.apiKey orNull), StringDataType),\n    (sessionToken, 3, userSeq map (_.data.sessionToken orNull), StringDataType),\n    (deviceToken, 5, userSeq map (_.data.deviceToken orNull), StringDataType),\n    (marketToken, 6, userSeq map (_.data.marketToken orNull), StringDataType),\n    (name, 7, userSeq map (_.data.name orNull), StringDataType),\n    (avatar, 8, userSeq map (_.data.avatar orNull), StringDataType),\n    (cover, 9, userSeq map (_.data.cover orNull), StringDataType),\n    (deviceName, 10, userSeq map (_.data.deviceName orNull), StringDataType),\n    (deviceCloudId, 11, userSeq map (_.data.deviceCloudId orNull), StringDataType))\n\n  prepareCursor[User](userSeq.size, cursorData)\n}\n\ntrait EmptyUserMockCursor extends MockCursor with UserRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, Seq.empty, IntDataType),\n    (email, 1, Seq.empty, StringDataType),\n    (apiKey, 2, Seq.empty, StringDataType),\n    (sessionToken, 3, Seq.empty, StringDataType),\n    (deviceToken, 4, Seq.empty, StringDataType),\n    (marketToken, 5, Seq.empty, StringDataType),\n    (name, 6, Seq.empty, StringDataType),\n    (avatar, 7, Seq.empty, StringDataType),\n    (cover, 8, Seq.empty, StringDataType),\n    (deviceName, 9, Seq.empty, StringDataType),\n    (deviceCloudId, 10, Seq.empty, StringDataType))\n\n  prepareCursor[User](0, cursorData)\n}\n\nclass UserRepositorySpec extends UserRepositorySpecification with UserRepositoryTestData {\n\n  \"UserRepositoryClient component\" should {\n\n    \"addUser\" should {\n\n      \"return a User object with a valid request\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) returns testId\n\n          val result = userRepository.addUser(data = createUserData).value.run\n\n          result must beLike {\n            case Right(userResponse) =>\n              userResponse.id shouldEqual testId\n              userResponse.data.email should beSome(testEmail)\n          }\n\n          there was one(contentResolverWrapper).insert(mockUri, createUserValues, Seq(mockUri))\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) throws contentResolverException\n\n          val result = userRepository.addUser(data = createUserData).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n\n          there was one(contentResolverWrapper).insert(mockUri, createUserValues, Seq(mockUri))\n        }\n    }\n\n    \"deleteUsers\" should {\n\n      \"return a successful result when all the users are deleted\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper.delete(any, any, any, any) returns 1\n\n          val result = userRepository.deleteUsers().value.run\n          result shouldEqual Right(1)\n\n          there was one(contentResolverWrapper)\n            .delete(uri = mockUri, where = \"\", notificationUris = Seq(mockUri))\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper\n            .delete(any, any, any, any) throws contentResolverException thenReturn 1\n\n          val result = userRepository.deleteUsers().value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n\n          there was one(contentResolverWrapper)\n            .delete(uri = mockUri, where = \"\", notificationUris = Seq(mockUri))\n        }\n    }\n\n    \"deleteUser\" should {\n\n      \"return a successful result when a valid user id is given\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper.deleteById(any, any, any, any, any) returns 1\n\n          val result = userRepository.deleteUser(user).value.run\n          result shouldEqual Right(1)\n\n          there was one(contentResolverWrapper)\n            .deleteById(uri = mockUri, id = testId, notificationUris = Seq(mockUri))\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper\n            .deleteById(any, any, any, any, any) throws contentResolverException thenReturn 1\n\n          val result = userRepository.deleteUser(user).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n\n          there were one(contentResolverWrapper)\n            .deleteById(uri = mockUri, id = testId, notificationUris = Seq(mockUri))\n        }\n    }\n\n    \"findUserById\" should {\n\n      \"return a User object when a existing id is given\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper.findById[UserEntity](any, any, any, any, any, any)(any) returns Some(\n            userEntity)\n\n          val result = userRepository.findUserById(id = testId).value.run\n\n          result must beLike {\n            case Right(maybeUser) =>\n              maybeUser must beSome[User].which { user =>\n                user.id shouldEqual testId\n                user.data.email should beSome(testEmail)\n              }\n          }\n\n          there was one(contentResolverWrapper).findById(\n            uri = mockUri,\n            id = testId,\n            projection = allFields)(f = getEntityFromCursor(userEntityFromCursor))\n        }\n\n      \"return None when a non-existing id is given\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper.findById(any, any, any, any, any, any)(any) returns None\n\n          val result = userRepository.findUserById(id = testNonExistingId).value.run\n          result shouldEqual Right(None)\n\n          there was one(contentResolverWrapper).findById(\n            uri = mockUri,\n            id = testNonExistingId,\n            projection = allFields)(f = getEntityFromCursor(userEntityFromCursor))\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper.findById(any, any, any, any, any, any)(any) throws contentResolverException thenReturn None\n\n          val result = userRepository.findUserById(id = testId).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n\n          there was one(contentResolverWrapper).findById(\n            uri = mockUri,\n            id = testId,\n            projection = allFields)(f = getEntityFromCursor(userEntityFromCursor))\n        }\n    }\n\n    \"fetchUsers\" should {\n\n      \"return all User\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper.fetchAll(uri = mockUri, projection = allFields)(\n            f = getListFromCursor(userEntityFromCursor)) returns userEntitySeq\n          val result = userRepository.fetchUsers.value.run\n          result shouldEqual Right(userSeq)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper.fetchAll(uri = mockUri, projection = allFields)(\n            f = getListFromCursor(userEntityFromCursor)) throws contentResolverException\n          val result = userRepository.fetchUsers.value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n\n    }\n\n    \"fetchIterableUsers\" should {\n\n      \"return an IterableCursor of User \" in\n        new UserMockCursor with UserRepositoryScope {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n\n          val result = userRepository.fetchIterableUsers(where = testMockWhere).value.run\n\n          result must beLike {\n            case Right(iterator) =>\n              toSeq(iterator) shouldEqual userSeq\n          }\n\n          there was one(contentResolverWrapper)\n            .getCursor(mockUri, AppEntity.allFields, testMockWhere, Seq.empty, \"\")\n        }\n\n      \"return an a RepositoryException when a exception is thrown \" in\n        new UserMockCursor with UserRepositoryScope {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) throws contentResolverException\n\n          val result = userRepository.fetchIterableUsers(where = testMockWhere).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"updateUser\" should {\n\n      \"return a successful result when the user is updated\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper.updateById(any, any, any, any) returns 1\n\n          val result = userRepository.updateUser(item = user).value.run\n          result shouldEqual Right(1)\n\n          there was one(contentResolverWrapper).updateById(\n            uri = mockUri,\n            id = testId,\n            values = createUserValues,\n            notificationUris = Seq(mockUri))\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new UserRepositoryScope {\n\n          contentResolverWrapper\n            .updateById(any, any, any, any) throws contentResolverException thenReturn 1\n\n          val result = userRepository.updateUser(item = user).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n\n          there was one(contentResolverWrapper).updateById(\n            uri = mockUri,\n            id = testId,\n            values = createUserValues,\n            notificationUris = Seq(mockUri))\n        }\n    }\n\n    \"getEntityFromCursor\" should {\n\n      \"return None when an empty cursor is given\" in\n        new EmptyUserMockCursor with Scope {\n\n          val result = getEntityFromCursor(userEntityFromCursor)(mockCursor)\n\n          result must beNone\n        }\n\n      \"return a User object when a cursor with data is given\" in\n        new UserMockCursor with Scope {\n\n          val result = getEntityFromCursor(userEntityFromCursor)(mockCursor)\n\n          result must beSome[UserEntity].which { user =>\n            user.id shouldEqual userEntity.id\n            user.data shouldEqual userEntity.data\n          }\n        }\n    }\n\n    \"getListFromCursor\" should {\n\n      \"return an empty sequence when an empty cursor is given\" in\n        new EmptyUserMockCursor with Scope {\n\n          val result = getListFromCursor(userEntityFromCursor)(mockCursor)\n\n          result should beEmpty\n        }\n\n      \"return a User sequence when a cursor with data is given\" in\n        new UserMockCursor with Scope {\n\n          val result = getListFromCursor(userEntityFromCursor)(mockCursor)\n\n          result shouldEqual userEntitySeq\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/user/UserRepositoryTestData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.user\n\nimport cards.nine.repository.model.{User, UserData}\nimport cards.nine.repository.provider.{UserEntity, UserEntityData}\n\nimport scala.util.Random\n\ntrait UserRepositoryTestData {\n\n  val testId            = Random.nextInt(10)\n  val testNonExistingId = 15\n  val testEmail         = Random.nextString(10)\n  val testApiKey        = Random.nextString(10)\n  val testSessionToken  = Random.nextString(10)\n  val testDeviceToken   = Random.nextString(10)\n  val testAndroidToken  = Random.nextString(10)\n  val testName          = Random.nextString(10)\n  val testAvatar        = Random.nextString(10)\n  val testCover         = Random.nextString(10)\n  val testDeviceName    = Random.nextString(10)\n  val testDeviceCloudId = Random.nextString(10)\n  val testMockWhere     = \"mock-where\"\n\n  val userEntitySeq = createUserEntitySeq(5)\n  val userEntity    = userEntitySeq.head\n  val userSeq       = createUserSeq(5)\n  val user          = userSeq.head\n\n  def createUserEntitySeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        UserEntity(\n          id = testId + i,\n          data = UserEntityData(\n            email = testEmail,\n            apiKey = testApiKey,\n            sessionToken = testSessionToken,\n            deviceToken = testDeviceToken,\n            marketToken = testAndroidToken,\n            name = testName,\n            avatar = testAvatar,\n            cover = testCover,\n            deviceName = testDeviceName,\n            deviceCloudId = testDeviceCloudId)))\n\n  def createUserSeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        User(\n          id = testId + i,\n          data = UserData(\n            email = Option(testEmail),\n            apiKey = Option(testApiKey),\n            sessionToken = Option(testSessionToken),\n            deviceToken = Option(testDeviceToken),\n            marketToken = Option(testAndroidToken),\n            name = Option(testName),\n            avatar = Option(testAvatar),\n            cover = Option(testCover),\n            deviceName = Option(testDeviceName),\n            deviceCloudId = Option(testDeviceCloudId))))\n\n  def createUserValues =\n    Map[String, Any](\n      UserEntity.email         -> testEmail,\n      UserEntity.apiKey        -> testApiKey,\n      UserEntity.sessionToken  -> testSessionToken,\n      UserEntity.deviceToken   -> testDeviceToken,\n      UserEntity.marketToken   -> testAndroidToken,\n      UserEntity.name          -> testName,\n      UserEntity.avatar        -> testAvatar,\n      UserEntity.cover         -> testCover,\n      UserEntity.deviceName    -> testDeviceName,\n      UserEntity.deviceCloudId -> testDeviceCloudId)\n\n  def createUserData =\n    UserData(\n      email = Option(testEmail),\n      apiKey = Option(testApiKey),\n      sessionToken = Option(testSessionToken),\n      deviceToken = Option(testDeviceToken),\n      marketToken = Option(testAndroidToken),\n      name = Option(testName),\n      avatar = Option(testAvatar),\n      cover = Option(testCover),\n      deviceName = Option(testDeviceName),\n      deviceCloudId = Option(testDeviceCloudId))\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/widget/WidgetRepositorySpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.widget\n\nimport android.net.Uri\nimport cards.nine.commons._\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapperImpl, UriCreator}\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.repository.{IntDataType, MockCursor, StringDataType}\nimport cards.nine.models.IterableCursor._\nimport cards.nine.repository.RepositoryException\nimport cards.nine.repository.model.Widget\nimport cards.nine.repository.provider.WidgetEntity._\nimport cards.nine.repository.provider._\nimport cards.nine.repository.repositories._\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait WidgetRepositorySpecification extends Specification with DisjunctionMatchers with Mockito {\n\n  trait WidgetRepositoryScope extends Scope with WidgetRepositoryTestData {\n\n    lazy val contentResolverWrapper = mock[ContentResolverWrapperImpl]\n\n    lazy val uriCreator = mock[UriCreator]\n\n    lazy val widgetRepository = new WidgetRepository(contentResolverWrapper, uriCreator)\n\n    lazy val mockUri = mock[Uri]\n\n    uriCreator.parse(any) returns mockUri\n\n    val contentResolverException = new RuntimeException(\"Irrelevant message\")\n\n  }\n\n}\n\ntrait WidgetMockCursor extends MockCursor with WidgetRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, widgetSeq map (_.id), IntDataType),\n    (momentId, 1, widgetSeq map (_.data.momentId), IntDataType),\n    (packageName, 2, widgetSeq map (_.data.packageName), StringDataType),\n    (className, 3, widgetSeq map (_.data.className), StringDataType),\n    (appWidgetId, 4, widgetSeq map (_.data.appWidgetId), IntDataType),\n    (startX, 5, widgetSeq map (_.data.startX), IntDataType),\n    (startY, 6, widgetSeq map (_.data.startY), IntDataType),\n    (spanX, 7, widgetSeq map (_.data.spanX), IntDataType),\n    (spanY, 8, widgetSeq map (_.data.spanY), IntDataType),\n    (widgetType, 9, widgetSeq map (_.data.widgetType), StringDataType),\n    (label, 10, widgetSeq map (_.data.label getOrElse javaNull), StringDataType),\n    (imagePath, 11, widgetSeq map (_.data.imagePath getOrElse javaNull), StringDataType),\n    (intent, 12, widgetSeq map (_.data.intent getOrElse javaNull), StringDataType))\n\n  prepareCursor[Widget](widgetSeq.size, cursorData)\n}\n\ntrait EmptyWidgetMockCursor extends MockCursor with WidgetRepositoryTestData {\n\n  val cursorData = Seq(\n    (NineCardsSqlHelper.id, 0, Seq.empty, IntDataType),\n    (momentId, 1, Seq.empty, IntDataType),\n    (packageName, 2, Seq.empty, StringDataType),\n    (className, 3, Seq.empty, StringDataType),\n    (appWidgetId, 4, Seq.empty, StringDataType),\n    (startX, 5, Seq.empty, IntDataType),\n    (startY, 6, Seq.empty, IntDataType),\n    (spanX, 7, Seq.empty, IntDataType),\n    (spanY, 8, Seq.empty, IntDataType),\n    (widgetType, 9, Seq.empty, StringDataType),\n    (label, 10, Seq.empty, StringDataType),\n    (imagePath, 11, Seq.empty, StringDataType),\n    (intent, 12, Seq.empty, StringDataType))\n\n  prepareCursor[Widget](0, cursorData)\n}\n\nclass WidgetRepositorySpec extends WidgetRepositorySpecification {\n\n  \"WidgetRepositoryClient component\" should {\n\n    \"addWidget\" should {\n\n      \"return a Widget object with a valid request\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) returns testWidgetId\n          val result = widgetRepository.addWidget(data = createWidgetData).value.run\n\n          result must beLike {\n            case Right(widget) =>\n              widget.id shouldEqual testWidgetId\n              widget.data.packageName shouldEqual testPackageName\n          }\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.insert(any, any, any) throws contentResolverException\n          val result = widgetRepository.addWidget(data = createWidgetData).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"addWidgets\" should {\n\n      \"return a sequence of Widget objects with a valid request\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.inserts(any, any, any, any) returns widgetIdSeq\n          val result = widgetRepository.addWidgets(datas = widgetDataSeq).value.run\n\n          result must beLike {\n            case Right(widgets) =>\n              widgets map (_.id) shouldEqual widgetIdSeq\n              widgets map (_.data.packageName) shouldEqual (widgetSeq map (_.data.packageName))\n          }\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.inserts(any, any, any, any) throws contentResolverException\n          val result = widgetRepository.addWidgets(datas = widgetDataSeq).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"deleteWidgets\" should {\n\n      \"return a successful result when all widgets are deleted\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.delete(any, any, any, any) returns 1\n          val result = widgetRepository.deleteWidgets().value.run\n          result shouldEqual Right(1)\n\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.delete(any, any, any, any) throws contentResolverException\n          val result = widgetRepository.deleteWidgets().value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"deleteWidget\" should {\n\n      \"return a successful result when a valid widget id is given\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.deleteById(any, any, any, any, any) returns 1\n          val result = widgetRepository.deleteWidget(widget).value.run\n          result shouldEqual Right(1)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper\n            .deleteById(any, any, any, any, any) throws contentResolverException\n          val result = widgetRepository.deleteWidget(widget).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"findWidgetById\" should {\n\n      \"return a Widget object when a existing id is given\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.findById[WidgetEntity](any, any, any, any, any, any)(any) returns Some(\n            widgetEntity)\n          val result = widgetRepository.findWidgetById(id = testWidgetId).value.run\n\n          result must beLike {\n            case Right(maybeWidget) =>\n              maybeWidget must beSome[Widget].which { widget =>\n                widget.id shouldEqual testWidgetId\n                widget.data.packageName shouldEqual testPackageName\n              }\n          }\n        }\n\n      \"return None when a non-existing id is given\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.findById(any, any, any, any, any, any)(any) returns None\n          val result = widgetRepository.findWidgetById(id = testNonExistingWidgetId).value.run\n          result shouldEqual Right(None)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.findById(any, any, any, any, any, any)(any) throws contentResolverException\n          val result = widgetRepository.findWidgetById(id = testWidgetId).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"fetchWidgetByAppWidgetId\" should {\n\n      \"return a Widget object when a existing appWidgetId is given\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.fetch(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$appWidgetId = ?\",\n            whereParams = Seq(testAppWidgetId.toString),\n            orderBy = \"\")(f = getEntityFromCursor(widgetEntityFromCursor)) returns Some(\n            widgetEntity)\n\n          val result =\n            widgetRepository.fetchWidgetByAppWidgetId(appWidgetId = testAppWidgetId).value.run\n\n          result must beLike {\n            case Right(maybeWidget) =>\n              maybeWidget must beSome[Widget].which { widget =>\n                widget.id shouldEqual testWidgetId\n                widget.data.appWidgetId shouldEqual testAppWidgetId\n              }\n          }\n        }\n\n      \"return None when a non-existing appWidgetId is given\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.fetch(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$appWidgetId = ?\",\n            whereParams = Seq(testNonExistingAppWidgetId.toString),\n            orderBy = \"\")(f = getEntityFromCursor(widgetEntityFromCursor)) returns None\n\n          val result = widgetRepository\n            .fetchWidgetByAppWidgetId(appWidgetId = testNonExistingAppWidgetId)\n            .value\n            .run\n          result shouldEqual Right(None)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.fetch(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$appWidgetId = ?\",\n            whereParams = Seq(testAppWidgetId.toString),\n            orderBy = \"\")(f = getEntityFromCursor(widgetEntityFromCursor)) throws contentResolverException\n\n          val result =\n            widgetRepository.fetchWidgetByAppWidgetId(appWidgetId = testAppWidgetId).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"fetchWidgetsByMoment\" should {\n\n      \"return a Widget sequence when a existent moment id is given\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$momentId = ?\",\n            whereParams = Seq(testMomentId.toString),\n            orderBy = s\"${WidgetEntity.momentId} asc\")(\n            f = getListFromCursor(widgetEntityFromCursor)) returns widgetEntitySeq\n\n          val result = widgetRepository.fetchWidgetsByMoment(momentId = testMomentId).value.run\n          result shouldEqual Right(widgetSeq)\n        }\n\n      \"fetchWidgetsByMoment should return an empty sequence when a non-existent moment id is given\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$momentId = ?\",\n            whereParams = Seq(testNonExistingMomentId.toString),\n            orderBy = s\"${WidgetEntity.momentId} asc\")(\n            f = getListFromCursor(widgetEntityFromCursor)) returns Seq.empty\n\n          val result =\n            widgetRepository.fetchWidgetsByMoment(momentId = testNonExistingMomentId).value.run\n          result shouldEqual Right(Seq.empty)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = s\"$momentId = ?\",\n            whereParams = Seq(testMomentId.toString),\n            orderBy = s\"${WidgetEntity.momentId} asc\")(\n            f = getListFromCursor(widgetEntityFromCursor)) throws contentResolverException\n\n          val result = widgetRepository.fetchWidgetsByMoment(momentId = testMomentId).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"fetchWidgets\" should {\n\n      \"return all Widgets\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = \"\",\n            whereParams = Seq.empty,\n            orderBy = \"\")(f = getListFromCursor(widgetEntityFromCursor)) returns widgetEntitySeq\n\n          val result = widgetRepository.fetchWidgets().value.run\n          result shouldEqual Right(widgetSeq)\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = mockUri,\n            projection = allFields,\n            where = \"\",\n            whereParams = Seq.empty,\n            orderBy = \"\")(f = getListFromCursor(widgetEntityFromCursor)) throws contentResolverException\n\n          val result = widgetRepository.fetchWidgets().value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"fetchIterableWidgets\" should {\n\n      \"return an IterableCursor of Widget  \" in\n        new WidgetMockCursor with WidgetRepositoryScope {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n\n          val result = widgetRepository.fetchIterableWidgets(where = testMockWhere).value.run\n\n          result must beLike {\n            case Right(iterator) =>\n              toSeq(iterator) shouldEqual widgetSeq\n          }\n\n          there was one(contentResolverWrapper)\n            .getCursor(mockUri, AppEntity.allFields, testMockWhere, Seq.empty, \"\")\n        }\n\n      \"return an a RepositoryException when a exception is thrown \" in\n        new WidgetMockCursor with WidgetRepositoryScope {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) throws contentResolverException\n\n          val result = widgetRepository.fetchIterableWidgets(where = testMockWhere).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"updateWidget\" should {\n\n      \"return a successful result when the widget is updated\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.updateById(any, any, any, any) returns 1\n          val result = widgetRepository.updateWidget(widget = widget).value.run\n          result shouldEqual Right(1)\n\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.updateById(any, any, any, any) throws contentResolverException\n          val result = widgetRepository.updateWidget(widget = widget).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"updateWidgets\" should {\n\n      \"return a successful result when the widgets are updated\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.updateByIds(any, any, any, any) returns Seq(5)\n          val result = widgetRepository.updateWidgets(widgets = widgetSeq).value.run\n          result shouldEqual Right(Seq(5))\n        }\n\n      \"return a RepositoryException when a exception is thrown\" in\n        new WidgetRepositoryScope {\n\n          contentResolverWrapper.updateByIds(any, any, any, any) throws contentResolverException\n          val result = widgetRepository.updateWidgets(widgets = widgetSeq).value.run\n          result must beAnInstanceOf[Left[RepositoryException, _]]\n        }\n    }\n\n    \"getEntityFromCursor\" should {\n\n      \"return None when an empty cursor is given\" in\n        new EmptyWidgetMockCursor with Scope {\n\n          val result = getEntityFromCursor(widgetEntityFromCursor)(mockCursor)\n          result must beNone\n        }\n\n      \"return a Widget object when a cursor with data is given\" in\n        new WidgetMockCursor with Scope {\n\n          val result = getEntityFromCursor(widgetEntityFromCursor)(mockCursor)\n\n          result must beSome[WidgetEntity].which { widget =>\n            widget.id shouldEqual widgetEntity.id\n            widget.data shouldEqual widgetEntity.data\n          }\n        }\n    }\n\n    \"getListFromCursor\" should {\n\n      \"return an empty sequence when an empty cursor is given\" in\n        new EmptyWidgetMockCursor with Scope {\n\n          val result = getListFromCursor(widgetEntityFromCursor)(mockCursor)\n          result should beEmpty\n        }\n\n      \"return a Widget sequence when a cursor with data is given\" in\n        new WidgetMockCursor with Scope {\n\n          val result = getListFromCursor(widgetEntityFromCursor)(mockCursor)\n          result shouldEqual widgetEntitySeq\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/repository/src/test/scala/cards/nine/repository/widget/WidgetRepositoryTestData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.repository.widget\n\nimport cards.nine.repository.model.{Widget, WidgetData}\nimport cards.nine.repository.provider.{WidgetEntity, WidgetEntityData}\n\nimport scala.util.Random\n\ntrait WidgetRepositoryTestData {\n\n  val testWidgetId               = Random.nextInt(10)\n  val testNonExistingWidgetId    = 15\n  val testMomentId               = Random.nextInt(10)\n  val testNonExistingMomentId    = 15\n  val testPackageName            = Random.nextString(5)\n  val testClassName              = Random.nextString(5)\n  val testAppWidgetId            = Random.nextInt(10)\n  val testNonExistingAppWidgetId = 15\n  val testStartX                 = Random.nextInt(10)\n  val testStartY                 = Random.nextInt(10)\n  val testSpanX                  = Random.nextInt(10)\n  val testSpanY                  = Random.nextInt(10)\n  val testWidgetType             = Random.nextString(5)\n  val testLabel                  = Random.nextString(5)\n  val testImagePath              = Random.nextString(5)\n  val testIntent                 = Random.nextString(5)\n  val testLabelOption            = Option(testLabel)\n  val testImagePathOption        = Option(testImagePath)\n  val testIntentOption           = Option(testIntent)\n  val testMockWhere              = \"mock-where\"\n\n  val widgetEntitySeq      = createWidgetEntitySeq(5)\n  val widgetEntity         = widgetEntitySeq(0)\n  val widgetSeq            = createWidgetSeq(5)\n  val widget               = widgetSeq(0)\n  val widgetIdSeq          = widgetSeq map (_.id)\n  val widgetDataSeq        = widgetSeq map (_.data)\n  val widgetValuesSeq      = createWidgetValuesSeq(5)\n  val widgetValues         = widgetValuesSeq(0)\n  val widgetIdAndValuesSeq = createWidgetIdAndValuesSeq(5)\n\n  def createWidgetEntitySeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        WidgetEntity(\n          id = testWidgetId + i,\n          data = WidgetEntityData(\n            momentId = testMomentId,\n            packageName = testPackageName,\n            className = testClassName,\n            appWidgetId = testAppWidgetId,\n            startX = testStartX,\n            startY = testStartY,\n            spanX = testSpanX,\n            spanY = testSpanY,\n            widgetType = testWidgetType,\n            label = testLabel,\n            imagePath = testImagePath,\n            intent = testIntent)))\n\n  def createWidgetSeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        Widget(\n          id = testWidgetId + i,\n          data = WidgetData(\n            momentId = testMomentId,\n            packageName = testPackageName,\n            className = testClassName,\n            appWidgetId = testAppWidgetId,\n            startX = testStartX,\n            startY = testStartY,\n            spanX = testSpanX,\n            spanY = testSpanY,\n            widgetType = testWidgetType,\n            label = testLabelOption,\n            imagePath = testImagePathOption,\n            intent = testIntentOption)))\n\n  def createWidgetValuesSeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        Map[String, Any](\n          WidgetEntity.momentId    -> testMomentId,\n          WidgetEntity.packageName -> testPackageName,\n          WidgetEntity.className   -> testClassName,\n          WidgetEntity.appWidgetId -> testAppWidgetId,\n          WidgetEntity.startX      -> testStartX,\n          WidgetEntity.startY      -> testStartY,\n          WidgetEntity.spanX       -> testSpanX,\n          WidgetEntity.spanY       -> testSpanY,\n          WidgetEntity.widgetType  -> testWidgetType,\n          WidgetEntity.label       -> (testLabelOption orNull),\n          WidgetEntity.imagePath   -> (testImagePathOption orNull),\n          WidgetEntity.intent      -> (testIntentOption orNull)))\n\n  def createWidgetIdAndValuesSeq(num: Int) =\n    List.tabulate(num)(\n      i =>\n        (testWidgetId + i,\n         Map[String, Any](\n           WidgetEntity.momentId    -> testMomentId,\n           WidgetEntity.packageName -> testPackageName,\n           WidgetEntity.className   -> testClassName,\n           WidgetEntity.appWidgetId -> testAppWidgetId,\n           WidgetEntity.startX      -> testStartX,\n           WidgetEntity.startY      -> testStartY,\n           WidgetEntity.spanX       -> testSpanX,\n           WidgetEntity.spanY       -> testSpanY,\n           WidgetEntity.widgetType  -> testWidgetType,\n           WidgetEntity.label       -> (testLabelOption orNull),\n           WidgetEntity.imagePath   -> (testImagePathOption orNull),\n           WidgetEntity.intent      -> (testIntentOption orNull))))\n\n  def createWidgetData =\n    WidgetData(\n      momentId = testMomentId,\n      packageName = testPackageName,\n      className = testClassName,\n      appWidgetId = testAppWidgetId,\n      startX = testStartX,\n      startY = testStartY,\n      spanX = testSpanX,\n      spanY = testSpanY,\n      widgetType = testWidgetType,\n      label = testLabelOption,\n      imagePath = testImagePathOption,\n      intent = testIntentOption)\n}\n"
  },
  {
    "path": "modules/services/build.sbt",
    "content": "android.Plugin.androidBuild\n\nplatformTarget in Android := \"android-24\"\n"
  },
  {
    "path": "modules/services/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      package=\"cards.nine.services\"\n      android:versionCode=\"1\"\n      android:versionName=\"1.0\">\n    <uses-sdk\n        android:minSdkVersion=\"16\"\n        android:targetSdkVersion=\"24\"/>\n\n    <application />\n</manifest>\n"
  },
  {
    "path": "modules/services/src/main/java/cards/nine/services/contacts/Fields.java",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.contacts;\n\nimport android.annotation.TargetApi;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.provider.CallLog;\nimport android.provider.ContactsContract;\n\n@TargetApi(Build.VERSION_CODES.HONEYCOMB)\npublic interface Fields {\n\n    // -- Base Contact -- //\n    Uri CONTENT_URI = ContactsContract.Contacts.CONTENT_URI;\n    Uri PHOTO_URI = ContactsContract.Contacts.CONTENT_LOOKUP_URI;\n    String LOOKUP_KEY = ContactsContract.Contacts.LOOKUP_KEY;\n    String DISPLAY_NAME = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY;\n    String HAS_PHONE_NUMBER = ContactsContract.Contacts.HAS_PHONE_NUMBER;\n    String STARRED = ContactsContract.Contacts.STARRED;\n\n    String ALL_CONTACTS_SELECTION = ContactsContract.Contacts.IN_VISIBLE_GROUP + \" = 1 \" +\n            \" AND \" +\n            ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + \" NOT NULL \" +\n            \" AND \" +\n            ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + \" <> ''\";\n    String CONTACTS_ORDER_BY_ASC = DISPLAY_NAME + \" COLLATE NOCASE ASC\";\n    String CONTACTS_BY_KEYWORD_SELECTION = ContactsContract.Contacts.IN_VISIBLE_GROUP + \" = 1 \" +\n            \" AND \" +\n            ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + \" LIKE ? \" +\n            \" AND \" +\n            ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + \" NOT NULL \" +\n            \" AND \" +\n            ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + \" <> ''\";\n    String LOOKUP_SELECTION = ContactsContract.Contacts.LOOKUP_KEY + \" = ?\";\n    String STARRED_SELECTION = ContactsContract.Contacts.STARRED + \" > 0\";\n    String HAS_PHONE_NUMBER_SELECTION = ContactsContract.Contacts.HAS_PHONE_NUMBER + \" = 1\";\n\n    // -- Email -- //\n    Uri EMAIL_CONTENT_URI = ContactsContract.CommonDataKinds.Email.CONTENT_URI;\n    String EMAIL_LOOKUP_KEY = ContactsContract.CommonDataKinds.Email.LOOKUP_KEY;\n    String EMAIL_DISPLAY_NAME = ContactsContract.CommonDataKinds.Email.DISPLAY_NAME_PRIMARY;\n    String EMAIL_HAS_PHONE_NUMBER = ContactsContract.CommonDataKinds.Email.HAS_PHONE_NUMBER;\n    String EMAIL_STARRED = ContactsContract.CommonDataKinds.Email.STARRED;\n    String EMAIL_ADDRESS = ContactsContract.CommonDataKinds.Email.ADDRESS;\n    String EMAIL_TYPE = ContactsContract.CommonDataKinds.Email.TYPE;\n\n    int EMAIL_TYPE_HOME = ContactsContract.CommonDataKinds.Email.TYPE_HOME;\n    int EMAIL_TYPE_WORK = ContactsContract.CommonDataKinds.Email.TYPE_WORK;\n\n    String EMAIL_CONTACT_SELECTION = ContactsContract.CommonDataKinds.Email.LOOKUP_KEY + \" IN \";\n    String EMAIL_SELECTION = ContactsContract.CommonDataKinds.Email.DATA + \" = ? \";\n\n    // -- Phone -- //\n    Uri PHONE_LOOKUP_URI = ContactsContract.PhoneLookup.CONTENT_FILTER_URI;\n    Uri PHONE_CONTENT_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;\n\n    String PHONE_LOOKUP_KEY = ContactsContract.PhoneLookup.LOOKUP_KEY;\n    String PHONE_DISPLAY_NAME = ContactsContract.PhoneLookup.DISPLAY_NAME;\n    String PHONE_HAS_PHONE_NUMBER = ContactsContract.PhoneLookup.HAS_PHONE_NUMBER;\n    String PHONE_STARRED = ContactsContract.PhoneLookup.STARRED;\n    String PHONE_NUMBER = ContactsContract.CommonDataKinds.Phone.NUMBER;\n    String PHONE_TYPE = ContactsContract.CommonDataKinds.Phone.TYPE;\n    String PHONE_CUSTOM_RINGTONE = ContactsContract.CommonDataKinds.Phone.CUSTOM_RINGTONE;\n\n    int PHONE_TYPE_MOBILE = ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE;\n    int PHONE_TYPE_WORK = ContactsContract.CommonDataKinds.Phone.TYPE_WORK;\n    int PHONE_TYPE_HOME = ContactsContract.CommonDataKinds.Phone.TYPE_HOME;\n    int PHONE_TYPE_MAIN = ContactsContract.CommonDataKinds.Phone.TYPE_MAIN;\n    int PHONE_TYPE_FAX_WORK = ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK;\n    int PHONE_TYPE_FAX_HOME = ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME;\n    int PHONE_TYPE_PAGER = ContactsContract.CommonDataKinds.Phone.TYPE_PAGER;\n\n    String PHONE_CONTACT_SELECTION = ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY + \" IN \";\n\n    // -- Calls -- //\n    Uri CALL_CONTENT_URI = CallLog.Calls.CONTENT_URI;\n\n    String CALL_NUMBER = CallLog.Calls.NUMBER;\n    String CALL_NAME = CallLog.Calls.CACHED_NAME;\n    String CALL_NUMBER_TYPE = CallLog.Calls.CACHED_NUMBER_TYPE;\n    String CALL_DATE= CallLog.Calls.DATE;\n    String CALL_TYPE = CallLog.Calls.TYPE;\n\n}"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/api/ApiServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.api\n\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models._\n\ntrait ApiServices {\n\n  /**\n   * Tries to login with the email and the device against backend V1\n   *\n   * @param email user email\n   * @param device user device\n   * @return the [[cards.nine.models.LoginResponseV1]]\n   * @throws ApiServiceV1ConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user is not found or the request throws an Exception\n   */\n  def loginV1(email: String, device: Device): TaskService[LoginResponseV1]\n\n  /**\n   * Fetches the user configuration associated to the user identified by the data in [[cards.nine.models.RequestConfigV1]]\n   *\n   * @return the [[cards.nine.models.UserV1]] with the [[UserV1]]\n   * @throws ApiServiceV1ConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def getUserConfigV1()(implicit requestConfig: RequestConfigV1): TaskService[UserV1]\n\n  /**\n   * Tries to login with the email, the androidId and the tokenId\n   *\n   * @param email user email\n   * @param androidId device identifier\n   * @param tokenId token id obtained in the email authentication\n   * @return the [[cards.nine.models.LoginResponse]]\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user is not found or the request throws an Exception\n   */\n  def login(email: String, androidId: String, tokenId: String): TaskService[LoginResponse]\n\n  /**\n   * Updates an existing user installation\n   *\n   * @param deviceToken the token used for push notification\n   * @param requestConfig necessary info for the headers\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if there was an error in the request\n   */\n  def updateInstallation(deviceToken: Option[String])(\n      implicit requestConfig: RequestConfig): TaskService[Unit]\n\n  /**\n   * Fetches the package info from Google Play given a package name\n   *\n   * @param packageName the package identifier. For example `com.fortysevendeg.ninecardslauncher`\n   * @param requestConfig necessary info for the headers\n   * @return the [[cards.nine.models.CategorizedPackage]]\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if there was an error in the request\n   */\n  def googlePlayPackage(packageName: String)(\n      implicit requestConfig: RequestConfig): TaskService[CategorizedPackage]\n\n  /**\n   * Fetches a list of packages information from Google Play given a list of package names. The response is similar to\n   * {@link #googlePlayPackage(String)(RequestConfig) googlePlayPackage} but allow to fetch a list of packages with one operation.\n   *\n   * @param packageNames a sequence of package identifiers\n   * @param requestConfig necessary info for the headers\n   * @return the sequence of [[cards.nine.models.CategorizedPackage]]\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if there was an error in the request\n   */\n  def googlePlayPackages(packageNames: Seq[String])(\n      implicit requestConfig: RequestConfig): TaskService[Seq[CategorizedPackage]]\n\n  /**\n   * Fetches a list of packages information from Google Play given a list of package names.\n   * Differs from googlePlayPackages by providing more information\n   *\n   * @param packageNames a sequence of package identifiers\n   * @param requestConfig necessary info for the headers\n   * @return the sequence of [[cards.nine.models.CategorizedDetailPackage]]\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if there was an error in the request\n   */\n  def googlePlayPackagesDetail(packageNames: Seq[String])(\n      implicit requestConfig: RequestConfig): TaskService[Seq[CategorizedDetailPackage]]\n\n  /**\n   * Fetches the recommended applications based on a category\n   *\n   * @param category the category\n   * @param excludePackages sequence of exclude packages\n   * @param limit the maximum number of apps returned\n   * @return the Seq[[cards.nine.models.NotCategorizedPackage]] of recommended apps\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def getRecommendedApps(category: String, excludePackages: Seq[String], limit: Int)(\n      implicit requestConfig: RequestConfig): TaskService[Seq[NotCategorizedPackage]]\n\n  /**\n   * Fetches the recommended applications based on other packages\n   *\n   * @param packages the liked packages\n   * @param excludePackages sequence of exclude packages\n   * @param limit the maximum number of apps returned\n   * @return the Seq[[cards.nine.models.NotCategorizedPackage]] of recommended apps\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def getRecommendedAppsByPackages(\n      packages: Seq[String],\n      excludePackages: Seq[String],\n      limit: Int)(implicit requestConfig: RequestConfig): TaskService[Seq[NotCategorizedPackage]]\n\n  /**\n   * Fetches the public collection\n   *\n   * @param sharedCollectionId the public collection id\n   * @return the TaskService containing a SharedCollection with the\n   *         collection or ApiServiceException if the user doesn't exists or there was an error in the request\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def getSharedCollection(sharedCollectionId: String)(\n      implicit requestConfig: RequestConfig): TaskService[SharedCollection]\n\n  /**\n   * Fetches the public collections based on some request params\n   *\n   * @param category category of collections\n   * @param collectionType type [top or latest]\n   * @param offset offset of list\n   * @param limit the maximum number of collection returned\n   * @return the [[cards.nine.models.SharedCollection]] with the sequence of recommended collections\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def getSharedCollectionsByCategory(\n      category: String,\n      collectionType: String,\n      offset: Int,\n      limit: Int)(implicit requestConfig: RequestConfig): TaskService[Seq[SharedCollection]]\n\n  /**\n   * Fetches the published collections\n   *\n   * @return the [[cards.nine.models.SharedCollection]] with the sequence of recommended collections\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def getPublishedCollections()(\n      implicit requestConfig: RequestConfig): TaskService[Seq[SharedCollection]]\n\n  /**\n   * Persists a new shared collection\n   *\n   * @param name The name of the collection\n   * @param author The original author of the collection\n   * @param packages The list of packages in the collection\n   * @param category the category of the SharedCollection\n   * @param icon The collection's icon\n   * @param community A flag for whether this is a community collection\n   * @return a String with the sharedCollectionId\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the service is unable to create the shared collection\n   */\n  def createSharedCollection(\n      name: String,\n      author: String,\n      packages: Seq[String],\n      category: String,\n      icon: String,\n      community: Boolean)(implicit requestConfig: RequestConfig): TaskService[String]\n\n  /**\n   * Updates an existing  shared collection\n   *\n   * @param sharedCollectionId The collection identifier\n   * @param name The name of the collection\n   * @param packages The list of packages in the collection\n   * @return a String with the sharedCollectionId\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the service is unable to create the shared collection\n   */\n  def updateSharedCollection(\n      sharedCollectionId: String,\n      name: Option[String],\n      packages: Seq[String])(implicit requestConfig: RequestConfig): TaskService[String]\n\n  /**\n   * Fetches the subscriptions\n   *\n   * @return the Seq[String] with the subscriptions\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def getSubscriptions()(implicit requestConfig: RequestConfig): TaskService[Seq[String]]\n\n  /**\n   * Subscribes to a public collection\n   *\n   * @param originalSharedCollectionId the public id of the collection to subscribe on\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def subscribe(originalSharedCollectionId: String)(\n      implicit requestConfig: RequestConfig): TaskService[Unit]\n\n  /**\n   * Unsubscribes from a public collection\n   *\n   * @param originalSharedCollectionId the public id of the collection to unsubscribe from\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def unsubscribe(originalSharedCollectionId: String)(\n      implicit requestConfig: RequestConfig): TaskService[Unit]\n\n  /**\n   * Updates an existing shared collectionn\n   *\n   * @param sharedCollectionId The collection identifier\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def updateViewShareCollection(sharedCollectionId: String)(\n      implicit requestConfig: RequestConfig): TaskService[Unit]\n\n  /**\n   * Rank the packages by importance inside their category\n   *\n   * @param packagesByCategorySeq a Sequence with the packages of the apps to rank ordered by its category\n   * @param location the current country location of the device if it can be obtained\n   * @return the Seq[[cards.nine.models.RankApps]]\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def rankApps(packagesByCategorySeq: Seq[PackagesByCategory], location: Option[String])(\n      implicit requestConfig: RequestConfig): TaskService[Seq[RankApps]]\n\n  /**\n   * Rank the packages by importance inside a moment\n   *\n   * @param packages a Sequence with the packages of the apps to rank\n   * @param moments a Sequence with the moments in which the apps must be ranked\n   * @param location the current country location of the device if it can be obtained\n   * @param limit the maximum number of packages to rank inside every moment\n   * @return the Seq[[cards.nine.models.RankAppsByMoment]]\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def rankAppsByMoment(\n      packages: Seq[String],\n      moments: Seq[String],\n      location: Option[String],\n      limit: Int)(implicit requestConfig: RequestConfig): TaskService[Seq[RankAppsByMoment]]\n\n  /**\n   * Rank the widgets by importance inside a moment\n   *\n   * @param packages a Sequence with the packages of the apps to rank its widgets\n   * @param moments a Sequence with the moments in which the apps' widgets must be ranked\n   * @param location the current country location of the device if it can be obtained\n   * @param limit the maximum number of widgets to rank inside every moment\n   * @return the Seq[[cards.nine.models.RankWidgetsByMoment]]\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def rankWidgetsByMoment(\n      packages: Seq[String],\n      moments: Seq[String],\n      location: Option[String],\n      limit: Int)(implicit requestConfig: RequestConfig): TaskService[Seq[RankWidgetsByMoment]]\n\n  /**\n   * Search apps based on a query string\n   *\n   * @param query the query string\n   * @param excludePackages sequence of exclude packages\n   * @param limit the maximum number of apps returned\n   * @return the Seq[[cards.nine.models.NotCategorizedPackage]]\n   * @throws ApiServiceConfigurationException if the configuration is not valid or can't be found\n   * @throws ApiServiceException if the user doesn't exists or there was an error in the request\n   */\n  def searchApps(query: String, excludePackages: Seq[String], limit: Int)(\n      implicit requestConfig: RequestConfig): TaskService[Seq[NotCategorizedPackage]]\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/api/Conversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.api\n\nimport cards.nine.api._\nimport cards.nine.api.version2.{\n  RankAppsCategoryResponse,\n  RankWidgetsResponse,\n  RankWidgetsWithMomentResponse\n}\nimport cards.nine.models._\nimport cards.nine.models.types._\nimport org.joda.time.format.DateTimeFormat\n\nimport scala.collection.immutable.Iterable\nimport scala.util.{Success, Try}\n\ntrait Conversions {\n\n  def toUser(\n      email: String,\n      device: Device\n  ): cards.nine.api.version1.User =\n    version1.User(\n      _id = None,\n      email = None,\n      sessionToken = None,\n      username = None,\n      password = None,\n      authData = Some(\n        version1.AuthData(\n          google = Some(\n            version1.AuthGoogle(\n              email = email,\n              devices = List(fromGoogleDevice(device))\n            )),\n          facebook = None,\n          twitter = None,\n          anonymous = None)))\n\n  def fromGoogleDevice(device: Device): cards.nine.api.version1.AuthGoogleDevice =\n    version1.AuthGoogleDevice(\n      name = device.name,\n      deviceId = device.deviceId,\n      secretToken = device.secretToken,\n      permissions = device.permissions)\n\n  def toLoginResponseV1(statusCode: Int, user: cards.nine.api.version1.User): LoginResponseV1 =\n    LoginResponseV1(\n      userId = user._id,\n      sessionToken = user.sessionToken,\n      email = user.email,\n      devices = (for {\n        data   <- user.authData\n        google <- data.google\n      } yield toGoogleDeviceSeq(google.devices)) getOrElse Seq.empty)\n\n  def toGoogleDeviceSeq(devices: Seq[cards.nine.api.version1.AuthGoogleDevice]): Seq[Device] =\n    devices map toGoogleDevice\n\n  def toGoogleDevice(device: cards.nine.api.version1.AuthGoogleDevice): Device =\n    Device(\n      name = device.name,\n      deviceId = device.deviceId,\n      secretToken = device.secretToken,\n      permissions = device.permissions)\n\n  def toCategorizedPackage(\n      packageName: String,\n      categorizeResponse: cards.nine.api.version2.CategorizeResponse): CategorizedPackage =\n    CategorizedPackage(\n      packageName = packageName,\n      category = categorizeResponse.items\n        .find(_.packageName == packageName)\n        .flatMap(app => findBestCategory(app.categories)))\n\n  def toCategorizedPackages(\n      categorizeResponse: cards.nine.api.version2.CategorizeResponse): Seq[CategorizedPackage] =\n    categorizeResponse.items.map(app =>\n      CategorizedPackage(app.packageName, findBestCategory(app.categories)))\n\n  def toCategorizedDetailPackages(\n      categorizeResponse: cards.nine.api.version2.CategorizeDetailResponse): Seq[\n    CategorizedDetailPackage] =\n    categorizeResponse.items.map { app =>\n      CategorizedDetailPackage(\n        packageName = app.packageName,\n        title = app.title,\n        category = findBestCategory(app.categories),\n        icon = app.icon,\n        free = app.free,\n        downloads = app.downloads,\n        stars = app.stars)\n    }\n\n  def toUserV1(apiUserConfig: cards.nine.api.version1.UserConfig): UserV1 =\n    UserV1(\n      _id = apiUserConfig._id,\n      email = apiUserConfig.email,\n      plusProfile = toUserConfigPlusProfile(apiUserConfig.plusProfile),\n      devices = apiUserConfig.devices map toUserConfigDevice,\n      status = toUserConfigStatusInfo(apiUserConfig.status))\n\n  def toUserConfigPlusProfile(\n      apiPlusProfile: cards.nine.api.version1.UserConfigPlusProfile): UserV1PlusProfile =\n    UserV1PlusProfile(\n      displayName = apiPlusProfile.displayName,\n      profileImage = toUserConfigProfileImage(apiPlusProfile.profileImage))\n\n  def toUserConfigProfileImage(\n      apiProfileImage: cards.nine.api.version1.UserConfigProfileImage): UserV1ProfileImage =\n    UserV1ProfileImage(imageType = apiProfileImage.imageType, imageUrl = apiProfileImage.imageUrl)\n\n  def toUserConfigDevice(apiDevice: cards.nine.api.version1.UserConfigDevice): UserV1Device =\n    UserV1Device(\n      deviceId = apiDevice.deviceId,\n      deviceName = apiDevice.deviceName,\n      collections = apiDevice.collections map toUserConfigCollection)\n\n  def toUserConfigCollection(\n      apiCollection: cards.nine.api.version1.UserConfigCollection): UserV1Collection =\n    UserV1Collection(\n      name = apiCollection.name,\n      originalSharedCollectionId = apiCollection.originalSharedCollectionId,\n      sharedCollectionId = apiCollection.sharedCollectionId,\n      sharedCollectionSubscribed = apiCollection.sharedCollectionSubscribed,\n      items = apiCollection.items map toUserConfigCollectionItem,\n      collectionType = CollectionType(apiCollection.collectionType),\n      constrains = apiCollection.constrains,\n      wifi = apiCollection.wifi,\n      occurrence = apiCollection.occurrence,\n      icon = apiCollection.icon,\n      category = apiCollection.category map (NineCardsCategory(_)))\n\n  def toUserConfigCollectionItem(\n      apiCollectionItem: cards.nine.api.version1.UserConfigCollectionItem): UserV1CollectionItem =\n    UserV1CollectionItem(\n      itemType = CardType(apiCollectionItem.itemType),\n      title = apiCollectionItem.title,\n      intent = apiCollectionItem.metadata.toString(),\n      categories =\n        apiCollectionItem.categories.map(categorySeq => categorySeq map (NineCardsCategory(_))))\n\n  def toUserConfigStatusInfo(\n      apiStatusInfo: cards.nine.api.version1.UserConfigStatusInfo): UserV1StatusInfo =\n    UserV1StatusInfo(\n      products = apiStatusInfo.products,\n      friendsReferred = apiStatusInfo.friendsReferred,\n      themesShared = apiStatusInfo.themesShared,\n      collectionsShared = apiStatusInfo.collectionsShared,\n      customCollections = apiStatusInfo.customCollections,\n      earlyAdopter = apiStatusInfo.earlyAdopter,\n      communityMember = apiStatusInfo.communityMember,\n      joinedThrough = apiStatusInfo.joinedThrough,\n      tester = apiStatusInfo.tester)\n\n  def toNotCategorizedPackageSeq(\n      apps: Seq[cards.nine.api.version2.NotCategorizedApp]): Seq[NotCategorizedPackage] =\n    apps map toNotCategorizedPackage\n\n  def toNotCategorizedPackage(\n      app: cards.nine.api.version2.NotCategorizedApp): NotCategorizedPackage =\n    NotCategorizedPackage(\n      packageName = app.packageName,\n      title = app.title,\n      icon = Option(app.icon),\n      downloads = app.downloads,\n      stars = app.stars,\n      free = app.free,\n      screenshots = app.screenshots)\n\n  def toSharedCollectionSeq(\n      collections: Seq[cards.nine.api.version2.Collection]): Seq[SharedCollection] =\n    collections map toSharedCollection\n\n  def formatPublishedDate(date: String): Long = {\n\n    val formatter = DateTimeFormat.forPattern(\"yyyy-MM-dd'T'HH:mm:ss.SSS\")\n\n    def cleanString: String = date.replaceAll(\"\\\"\", \"\") match {\n      case s if s.matches(\".+\\\\.\\\\d{3}000\") => s.substring(0, s.length - 3)\n      case s                                => s\n    }\n\n    Try(formatter.withZoneUTC().parseDateTime(cleanString)) match {\n      case Success(d) => d.getMillis\n      case _          => 0\n    }\n  }\n\n  def toSharedCollection(collection: cards.nine.api.version2.Collection) =\n    SharedCollection(\n      id = collection.publicIdentifier,\n      sharedCollectionId = collection.publicIdentifier,\n      publishedOn = formatPublishedDate(collection.publishedOn),\n      author = collection.author,\n      name = collection.name,\n      packages = collection.packages,\n      resolvedPackages = toSharedCollectionPackageSeq(collection.appsInfo),\n      views = collection.views getOrElse 0,\n      subscriptions = collection.subscriptions,\n      category = NineCardsCategory(collection.category),\n      icon = collection.icon,\n      community = collection.community,\n      locallyAdded = None,\n      publicCollectionStatus = if (collection.owned) PublishedByMe else PublishedByOther)\n\n  def toSharedCollectionPackageSeq(\n      packages: Seq[cards.nine.api.version2.CollectionApp]): Seq[SharedCollectionPackage] =\n    packages map toSharedCollectionPackage\n\n  def toSharedCollectionPackage(\n      item: cards.nine.api.version2.CollectionApp): SharedCollectionPackage =\n    SharedCollectionPackage(\n      packageName = item.packageName,\n      title = item.title,\n      icon = item.icon,\n      category = findBestCategory(item.categories),\n      stars = item.stars,\n      downloads = item.downloads,\n      free = item.free)\n\n  def toItemsMap(packagesByCategorySeq: Seq[PackagesByCategory]) =\n    Map(\n      packagesByCategorySeq map (packagesByCategory =>\n                                   packagesByCategory.category.name -> packagesByCategory.packages): _*)\n\n  def toRankAppsResponse(items: Seq[RankAppsCategoryResponse]) =\n    items map (response =>\n                 RankApps(\n                   category = NineCardsCategory(response.category),\n                   packages = response.packages))\n\n  def toRankAppsByMomentResponse(items: Seq[RankAppsCategoryResponse]) =\n    items map (response =>\n                 RankAppsByMoment(\n                   moment = NineCardsMoment(response.category),\n                   packages = response.packages))\n\n  def toRankWidgetsByMomentResponse(\n      items: Seq[RankWidgetsWithMomentResponse]): Seq[RankWidgetsByMoment] =\n    items map (response =>\n                 RankWidgetsByMoment(\n                   moment = NineCardsMoment(response.moment),\n                   widgets = response.widgets map toRankWidgets))\n\n  def toRankWidgets(widget: RankWidgetsResponse) =\n    RankWidget(packageName = widget.packageName, className = widget.className)\n\n  private[this] def findBestCategory(categories: Seq[String]): Option[NineCardsCategory] =\n    categories.foldLeft[Option[NineCardsCategory]](None) {\n      case (Some(nineCardsCategory), _) => Some(nineCardsCategory)\n      case (_, categoryName) =>\n        (NineCardsCategory.gamesCategories ++ NineCardsCategory.appsCategories)\n          .find(_.name == categoryName)\n    }\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/api/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.api\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class ApiServiceException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class ApiServiceV1ConfigurationException(message: String)\n    extends RuntimeException(message)\n    with NineCardException {\n  override def cause: Option[Throwable] = None\n}\n\ncase class ApiServiceConfigurationException(message: String)\n    extends RuntimeException(message)\n    with NineCardException {\n  override def cause: Option[Throwable] = None\n}\n\ntrait ImplicitsApiServiceExceptions {\n  implicit def apiServiceExceptionConverter =\n    (t: Throwable) => ApiServiceException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/api/impl/ApiServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.api.impl\n\nimport cards.nine.api._\nimport cards.nine.api.rest.client.messages.ServiceClientResponse\nimport cards.nine.api.version2._\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models._\nimport cards.nine.services.api.Conversions\nimport cards.nine.services.api._\nimport monix.eval.Task\n\ncase class ApiServicesConfig(appId: String, appKey: String, localization: String)\n\nclass ApiServicesImpl(\n    apiServicesConfig: ApiServicesConfig,\n    apiService: cards.nine.api.version2.ApiService,\n    apiServiceV1: cards.nine.api.version1.ApiService)\n    extends ApiServices\n    with Conversions\n    with ImplicitsApiServiceExceptions {\n\n  import cards.nine.api.version1.JsonImplicits._\n  import cards.nine.api.version2.JsonImplicits._\n\n  val headerAppId = \"X-Appsly-Application-Id\"\n\n  val headerAppKey = \"X-Appsly-REST-API-Key\"\n\n  val headerDevice = \"X-Android-ID\"\n\n  val headerToken = \"X-Appsly-Session-Token\"\n\n  val headerLocalization = \"X-Android-Market-Localization\"\n\n  val userNotFoundMessage = \"User not found\"\n\n  val userNotAuthenticatedMessage = \"User not authenticated\"\n\n  val installationNotFoundMessage = \"Installation not found\"\n\n  val playAppNotFoundMessage = \"Google Play Package not found\"\n\n  val userConfigNotFoundMessage = \"User configuration not found\"\n\n  val categoryNotFoundMessage = \"Google Play Category not found\"\n\n  val subscriptionsNotFoundMessage = \"Subscriptions not found\"\n\n  val errorCreatingCollectionMessage = \"Unknown error creating collection\"\n\n  val shareCollectionNotFoundMessage = \"Shared Collections not found\"\n\n  val createSharedCollectionNotFoundMessage = \"Shared Collection not found\"\n\n  val publishedCollectionsNotFoundMessage = \"Published Collections not found\"\n\n  val errorRankingAppsMessage = \"Unknown error ranking apps\"\n\n  val errorRankingAppsByMomentMessage = \"Unknown error ranking apps by moment\"\n\n  val errorRankingWidgetsByMomentMessage = \"Unknown error ranking widgets by moment\"\n\n  val errorSearchingAppsMessage = \"Unknown error searching apps\"\n\n  override def loginV1(email: String, device: Device) =\n    for {\n      baseHeader <- prepareV1Header\n      response <- apiServiceV1\n        .login(toUser(email, device), baseHeader)\n        .readOption(userNotFoundMessage)\n    } yield toLoginResponseV1(response.statusCode, response.data)\n\n  override def getUserConfigV1()(implicit requestConfig: RequestConfigV1) =\n    for {\n      baseHeader <- prepareV1Header\n      header = baseHeader :+ ((headerDevice, requestConfig.deviceId)) :+ ((headerToken,\n                                                                           requestConfig.token))\n      response <- apiServiceV1.getUserConfig(header).readOption(userConfigNotFoundMessage)\n    } yield toUserV1(response.data)\n\n  override def login(email: String, androidId: String, tokenId: String) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .login(ApiLoginRequest(email, androidId, tokenId))\n        .readOption(userNotAuthenticatedMessage)\n    } yield LoginResponse(response.data.apiKey, response.data.sessionToken)\n\n  override def updateInstallation(deviceToken: Option[String])(\n      implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .installations(\n          InstallationRequest(deviceToken getOrElse \"\"),\n          requestConfig.toServiceHeader)\n        .readOption(installationNotFoundMessage)\n    } yield ()\n\n  override def googlePlayPackage(packageName: String)(implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .categorize(CategorizeRequest(Seq(packageName)), requestConfig.toGooglePlayHeader)\n        .readOption(playAppNotFoundMessage)\n    } yield toCategorizedPackage(packageName, response.data)\n\n  override def googlePlayPackages(packageNames: Seq[String])(\n      implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .categorize(CategorizeRequest(packageNames), requestConfig.toGooglePlayHeader)\n        .resolve[ApiServiceException]\n    } yield response.data map toCategorizedPackages getOrElse Seq.empty\n\n  override def googlePlayPackagesDetail(packageNames: Seq[String])(\n      implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .categorizeDetail(CategorizeRequest(packageNames), requestConfig.toGooglePlayHeader)\n        .resolve[ApiServiceException]\n    } yield response.data map toCategorizedDetailPackages getOrElse Seq.empty\n\n  override def getRecommendedApps(category: String, excludePackages: Seq[String], limit: Int)(\n      implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .recommendations(\n          category,\n          None,\n          RecommendationsRequest(excludePackages, limit),\n          requestConfig.toGooglePlayHeader)\n        .readOption(categoryNotFoundMessage)\n    } yield toNotCategorizedPackageSeq(response.data.items)\n\n  override def getRecommendedAppsByPackages(\n      packages: Seq[String],\n      excludePackages: Seq[String],\n      limit: Int)(implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .recommendationsByApps(\n          RecommendationsByAppsRequest(packages, excludePackages, limit),\n          requestConfig.toGooglePlayHeader)\n        .resolve[ApiServiceException]\n      apps = response.data.map(_.apps) getOrElse Seq.empty\n    } yield toNotCategorizedPackageSeq(apps)\n\n  override def getSharedCollection(sharedCollectionId: String)(\n      implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .getCollection(sharedCollectionId, requestConfig.toGooglePlayHeader)\n        .readOption(publishedCollectionsNotFoundMessage)\n    } yield toSharedCollection(response.data)\n\n  override def getSharedCollectionsByCategory(\n      category: String,\n      collectionType: String,\n      offset: Int,\n      limit: Int)(implicit requestConfig: RequestConfig) = {\n\n    def serviceCall(\n        header: ServiceMarketHeader): TaskService[ServiceClientResponse[CollectionsResponse]] =\n      collectionType.toLowerCase match {\n        case \"top\" =>\n          apiService.topCollections(category, offset, limit, header)\n        case \"latest\" =>\n          apiService.latestCollections(category, offset, limit, header)\n        case _ => TaskService(Task(Left(ApiServiceException(shareCollectionNotFoundMessage))))\n\n      }\n\n    for {\n      _ <- validateConfig\n      response <- serviceCall(requestConfig.toGooglePlayHeader)\n        .readOption(shareCollectionNotFoundMessage)\n    } yield toSharedCollectionSeq(response.data.collections)\n  }\n\n  override def getPublishedCollections()(implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .getCollections(requestConfig.toGooglePlayHeader)\n        .readOption(publishedCollectionsNotFoundMessage)\n    } yield toSharedCollectionSeq(response.data.collections)\n\n  override def createSharedCollection(\n      name: String,\n      author: String,\n      packages: Seq[String],\n      category: String,\n      icon: String,\n      community: Boolean)(implicit requestConfig: RequestConfig) = {\n\n    val request = version2.CreateCollectionRequest(\n      name = name,\n      author = author,\n      icon = icon,\n      category = category,\n      community = community,\n      packages = packages)\n\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .createCollection(request, requestConfig.toServiceHeader)\n        .readOption(errorCreatingCollectionMessage)\n    } yield response.data.publicIdentifier\n  }\n\n  override def updateSharedCollection(\n      sharedCollectionId: String,\n      maybeName: Option[String],\n      packages: Seq[String])(implicit requestConfig: RequestConfig) = {\n\n    def toUpdateInfo: Option[CollectionUpdateInfo] =\n      maybeName map (name => CollectionUpdateInfo(name))\n\n    val request =\n      version2.UpdateCollectionRequest(collectionInfo = toUpdateInfo, packages = Some(packages))\n\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .updateCollection(sharedCollectionId, request, requestConfig.toServiceHeader)\n        .readOption(errorCreatingCollectionMessage)\n    } yield response.data.publicIdentifier\n  }\n\n  override def getSubscriptions()(implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .getSubscriptions(requestConfig.toServiceHeader)\n        .readOption(subscriptionsNotFoundMessage)\n    } yield response.data.subscriptions\n\n  override def subscribe(sharedCollectionId: String)(implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .subscribe(sharedCollectionId, requestConfig.toServiceHeader)\n        .resolve[ApiServiceException]\n    } yield ()\n\n  override def unsubscribe(sharedCollectionId: String)(implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .unsubscribe(sharedCollectionId, requestConfig.toServiceHeader)\n        .resolve[ApiServiceException]\n    } yield ()\n\n  override def updateViewShareCollection(sharedCollectionId: String)(\n      implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .updateViewShareCollection(sharedCollectionId, requestConfig.toServiceHeader)\n        .resolve[ApiServiceException]\n    } yield ()\n\n  override def rankApps(packagesByCategorySeq: Seq[PackagesByCategory], location: Option[String])(\n      implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .rankApps(\n          RankAppsRequest(toItemsMap(packagesByCategorySeq), location),\n          requestConfig.toServiceHeader)\n        .readOption(errorRankingAppsMessage)\n    } yield toRankAppsResponse(response.data.items)\n\n  override def rankAppsByMoment(\n      packages: Seq[String],\n      moments: Seq[String],\n      location: Option[String],\n      limit: Int)(implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .rankAppsByMoment(\n          RankAppsByMomentRequest(packages, moments, location, limit),\n          requestConfig.toServiceHeader)\n        .readOption(errorRankingAppsByMomentMessage)\n    } yield toRankAppsByMomentResponse(response.data.items)\n\n  override def rankWidgetsByMoment(\n      packages: Seq[String],\n      moments: Seq[String],\n      location: Option[String],\n      limit: Int)(implicit requestConfig: RequestConfig) =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .rankWidgetsByMoment(\n          RankWidgetsByMomentRequest(packages, moments, location, limit),\n          requestConfig.toServiceHeader)\n        .readOption(errorRankingAppsByMomentMessage)\n    } yield toRankWidgetsByMomentResponse(response.data.items)\n\n  override def searchApps(query: String, excludePackages: Seq[String], limit: Int)(\n      implicit requestConfig: RequestConfig): TaskService[Seq[NotCategorizedPackage]] =\n    for {\n      _ <- validateConfig\n      response <- apiService\n        .search(SearchRequest(query, excludePackages, limit), requestConfig.toGooglePlayHeader)\n        .readOption(errorSearchingAppsMessage)\n    } yield toNotCategorizedPackageSeq(response.data.items)\n\n  private[this] def prepareV1Header: TaskService[Seq[(String, String)]] = {\n\n    def isConfigValid: Boolean =\n      apiServiceV1.baseUrl.nonEmpty &&\n        apiServicesConfig.appId.nonEmpty &&\n        apiServicesConfig.appKey.nonEmpty\n\n    TaskService {\n      Task {\n        if (isConfigValid) {\n          Right(\n            Seq(\n              (headerAppId, apiServicesConfig.appId),\n              (headerAppKey, apiServicesConfig.appKey),\n              (headerLocalization, apiServicesConfig.localization)))\n        } else {\n          Left(ApiServiceV1ConfigurationException(\"Invalid configuration\"))\n        }\n      }\n    }\n\n  }\n\n  case class ServiceResponse[T](statusCode: Int, data: T)\n\n  implicit class ServiceOptionExt[T](taskService: TaskService[ServiceClientResponse[T]]) {\n\n    def readOption(msg: String): TaskService[ServiceResponse[T]] =\n      taskService.resolveSides(mapRight = {\n        case ServiceClientResponse(statusCode, Some(value)) =>\n          Right(ServiceResponse(statusCode, value))\n        case _ => Left(ApiServiceException(msg))\n      }, mapLeft = e => Left(ApiServiceException(e.getMessage, Some(e))))\n\n  }\n\n  private[this] def validateConfig: TaskService[Unit] = TaskService {\n    Task {\n      if (apiService.baseUrl.nonEmpty) Right((): Unit)\n      else Left(ApiServiceConfigurationException(\"Invalid configuration\"))\n    }\n  }\n\n  implicit class RequestConfigExt(request: RequestConfig) {\n    def toServiceHeader: cards.nine.api.version2.ServiceHeader =\n      version2.ServiceHeader(request.apiKey, request.sessionToken, request.androidId)\n\n    def toGooglePlayHeader: cards.nine.api.version2.ServiceMarketHeader =\n      version2.ServiceMarketHeader(\n        request.apiKey,\n        request.sessionToken,\n        request.androidId,\n        request.marketToken)\n  }\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/apps/AppsServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.apps\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.ApplicationData\n\ntrait AppsServices {\n\n  /**\n   * Obtains a sequence with all the installed apps\n   *\n   * @return the Seq[cards.nine.models.ApplicationData] with the data of the apps\n   * @throws AppsInstalledException if exist some problem obtaining the installed apps\n   */\n  def getInstalledApplications(implicit context: ContextSupport): TaskService[Seq[ApplicationData]]\n\n  /**\n   * Obtains an installed app by the package name\n   *\n   * @param packageName the package name of the app to get\n   * @return the [cards.nine.models.ApplicationData] with the data of the app\n   * @throws AppsInstalledException if exist some problem obtaining the installed app\n   */\n  def getApplication(packageName: String)(\n      implicit context: ContextSupport): TaskService[ApplicationData]\n\n  /**\n   * Return a sequence with the default apps for ten predefined actions\n   *\n   * @return the Seq[cards.nine.models.ApplicationData] with the data of the apps\n   * @throws AppsInstalledException if there was an error with trying to get the default apps\n   */\n  def getDefaultApps(implicit context: ContextSupport): TaskService[Seq[ApplicationData]]\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/apps/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.apps\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class AppsInstalledException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsAppsExceptions {\n  implicit def appsInstalledExceptionConverter =\n    (t: Throwable) => AppsInstalledException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/apps/impl/AppsServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.apps.impl\n\nimport android.content.Intent\nimport android.content.pm.{PackageManager, ResolveInfo}\nimport android.provider.MediaStore\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.commons.{javaNull, CatchAll}\nimport cards.nine.models.ApplicationData\nimport cards.nine.models.types.Misc\nimport cards.nine.services.apps._\n\nimport scala.collection.JavaConversions._\n\nclass AppsServicesImpl extends AppsServices with ImplicitsAppsExceptions {\n\n  val androidFeedback = \"com.google.android.feedback\"\n  val androidVending  = \"com.android.vending\"\n\n  override def getInstalledApplications(implicit context: ContextSupport) =\n    getAppsByIntent(mainIntentByCategory(Intent.CATEGORY_LAUNCHER))\n\n  override def getApplication(packageName: String)(implicit context: ContextSupport) =\n    for {\n      pm          <- packageManagerTask\n      maybeIntent <- catchAll(Option(pm.getLaunchIntentForPackage(packageName)))\n      maybeResolveInfo <- catchAll(\n        maybeIntent flatMap (intent => Option(pm.resolveActivity(intent, 0))))\n      applicationData <- (maybeIntent, maybeResolveInfo) match {\n        case (Some(_), Some(resolveInfo)) => catchAll(getApplicationByResolveInfo(pm, resolveInfo))\n        case (None, _) =>\n          TaskService.left(\n            AppsInstalledException(s\"Received a null intent for package $packageName\"))\n        case (_, None) =>\n          TaskService.left(\n            AppsInstalledException(s\"Received a null resolve info for package $packageName\"))\n      }\n    } yield applicationData\n\n  override def getDefaultApps(implicit context: ContextSupport) =\n    for {\n      phoneApp <- getAppsByIntent(phoneIntent()).map(_.headOption)\n      messageApp <- getAppsByIntent(mainIntentByCategory(Intent.CATEGORY_APP_MESSAGING))\n        .map(_.headOption)\n      browserApp <- getAppsByIntent(mainIntentByCategory(Intent.CATEGORY_APP_BROWSER))\n        .map(_.headOption)\n      cameraApp <- getAppsByIntent(cameraIntent()).map(_.headOption)\n      emailApp <- getAppsByIntent(mainIntentByCategory(Intent.CATEGORY_APP_EMAIL))\n        .map(_.headOption)\n      mapsApp <- getAppsByIntent(mainIntentByCategory(Intent.CATEGORY_APP_MAPS)).map(_.headOption)\n      musicApp <- getAppsByIntent(mainIntentByCategory(Intent.CATEGORY_APP_MUSIC))\n        .map(_.headOption)\n      galleryApp <- getAppsByIntent(mainIntentByCategory(Intent.CATEGORY_APP_GALLERY))\n        .map(_.headOption)\n      calendarApp <- getAppsByIntent(mainIntentByCategory(Intent.CATEGORY_APP_CALENDAR))\n        .map(_.headOption)\n      marketApp <- getAppsByIntent(mainIntentByCategory(Intent.CATEGORY_APP_MARKET))\n        .map(_.headOption)\n    } yield\n      Seq(\n        phoneApp,\n        messageApp,\n        browserApp,\n        cameraApp,\n        emailApp,\n        mapsApp,\n        musicApp,\n        galleryApp,\n        calendarApp,\n        marketApp).flatten\n\n  private[this] def getAppsByIntent(intent: Intent)(\n      implicit context: ContextSupport): TaskService[Seq[ApplicationData]] =\n    for {\n      pm <- packageManagerTask\n      appsData <- catchAll(\n        pm.queryIntentActivities(intent, 0).toSeq map (getApplicationByResolveInfo(pm, _)))\n    } yield appsData.filterNot(_.packageName == context.getPackageName)\n\n  private[this] def getApplicationByResolveInfo(pm: PackageManager, resolveInfo: ResolveInfo)(\n      implicit context: ContextSupport) = {\n    val packageName = resolveInfo.activityInfo.applicationInfo.packageName\n    val className   = resolveInfo.activityInfo.name\n    val packageInfo = pm.getPackageInfo(packageName, 0)\n\n    ApplicationData(\n      name = resolveInfo.loadLabel(pm).toString,\n      packageName = packageName,\n      className = className,\n      category = Misc,\n      dateInstalled = packageInfo.firstInstallTime,\n      dateUpdated = packageInfo.lastUpdateTime,\n      version = packageInfo.versionCode.toString,\n      installedFromGooglePlay = isFromGooglePlay(pm, packageName))\n  }\n\n  private[this] def packageManagerTask(implicit context: ContextSupport) =\n    TaskService(CatchAll[AppsInstalledException](context.getPackageManager))\n\n  private[this] def catchAll[T](f: => T) = TaskService(CatchAll[AppsInstalledException](f))\n\n  private[this] def isFromGooglePlay(packageManager: PackageManager, packageName: String) = {\n    packageManager.getInstallerPackageName(packageName) match {\n      case `androidFeedback` => true\n      case `androidVending`  => true\n      case _                 => false\n    }\n  }\n\n  def mainIntentByCategory(category: String): Intent = {\n    val mainIntent: Intent = new Intent(Intent.ACTION_MAIN, javaNull)\n    mainIntent.addCategory(category)\n    mainIntent\n  }\n\n  def phoneIntent(): Intent = {\n    val intent: Intent = new Intent(Intent.ACTION_DIAL, javaNull)\n    intent.addCategory(Intent.CATEGORY_DEFAULT)\n    intent\n  }\n\n  def cameraIntent(): Intent = {\n    val intent: Intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA, javaNull)\n    intent.addCategory(Intent.CATEGORY_DEFAULT)\n    intent\n  }\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/awareness/AwarenessServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.awareness\n\nimport android.content.BroadcastReceiver\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.AwarenessFenceUpdate\nimport cards.nine.models.{Headphones, Location, ProbablyActivity, WeatherState}\n\ntrait AwarenessServices {\n\n  /**\n   * Return the most probably activity\n   *\n   * @return activity\n   * @throws AwarenessException if there was an error with the request GoogleDrive api\n   */\n  def getTypeActivity: TaskService[ProbablyActivity]\n\n  /**\n   * Register a pending intent for fence updates\n   * @param action the action for the intent\n   * @param fences fences to register for\n   * @param receiver that will receive the updates\n   */\n  def registerFenceUpdates(\n      action: String,\n      fences: Seq[AwarenessFenceUpdate],\n      receiver: BroadcastReceiver)(implicit contextSupport: ContextSupport): TaskService[Unit]\n\n  /**\n   * Register a pending intent for fence updates\n   * @param action the action for the intent\n   */\n  def unregisterFenceUpdates(action: String)(\n      implicit contextSupport: ContextSupport): TaskService[Unit]\n\n  /**\n   * Return headphone state\n   *\n   * @return if headphone is connected\n   * @throws AwarenessException if there was an error with the request GoogleDrive api\n   */\n  def getHeadphonesState: TaskService[Headphones]\n\n  /**\n   * Return information about current location\n   *\n   * @return current location\n   * @throws AwarenessException if there was an error with the request GoogleDrive api\n   */\n  def getLocation(implicit contextSupport: ContextSupport): TaskService[Location]\n\n  /**\n   * Return information about current weather\n   *\n   * @return current weather\n   * @throws AwarenessException if there was an error with the request GoogleDrive api\n   */\n  def getWeather: TaskService[WeatherState]\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/awareness/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.awareness\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class AwarenessException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsAwarenessExceptions {\n  implicit def awarenessExceptionConverter =\n    (t: Throwable) => AwarenessException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/calls/CallsContentProvider.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.calls\n\nimport android.database.Cursor\nimport cards.nine.models.Call\nimport cards.nine.models.types._\nimport cards.nine.services.contacts.Fields\n\nobject CallsContentProvider {\n\n  val allFields = Seq(\n    Fields.CALL_NUMBER,\n    Fields.CALL_NAME,\n    Fields.CALL_NUMBER_TYPE,\n    Fields.CALL_DATE,\n    Fields.CALL_TYPE)\n\n  def callFromCursor(cursor: Cursor) =\n    readCall(\n      cursor = cursor,\n      number = Fields.CALL_NUMBER,\n      name = Fields.CALL_NAME,\n      numberType = parseNumberType(cursor.getInt(cursor.getColumnIndex(Fields.CALL_NUMBER_TYPE))),\n      date = Fields.CALL_DATE,\n      callType = Fields.CALL_TYPE)\n\n  def parseNumberType(phoneType: Int): PhoneCategory =\n    phoneType match {\n      case Fields.PHONE_TYPE_HOME   => PhoneHome\n      case Fields.PHONE_TYPE_WORK   => PhoneWork\n      case Fields.PHONE_TYPE_MOBILE => PhoneMobile\n      case _                        => PhoneOther\n    }\n\n  private[this] def readCall(\n      cursor: Cursor,\n      number: String,\n      name: String,\n      numberType: PhoneCategory,\n      date: String,\n      callType: String) = {\n    Call(\n      number = cursor.getString(cursor.getColumnIndex(number)),\n      name = Option(cursor.getString(cursor.getColumnIndex(name))),\n      numberType = numberType,\n      date = cursor.getLong(cursor.getColumnIndex(date)),\n      callType = CallType(cursor.getInt(cursor.getColumnIndex(callType))))\n  }\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/calls/CallsServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.calls\n\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.Call\n\ntrait CallsServices {\n\n  /**\n   * Get the last calls in the system\n   *\n   * @return the Seq[cards.nine.models.Call] contains information about the widget\n   * @throws CallsServicesPermissionException if the permission for read calls hasn't been granted\n   * @throws CallsServicesException if exist some problem to get the calls in the cell phone\n   */\n  def getLastCalls: TaskService[Seq[Call]]\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/calls/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.calls\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class CallsServicesException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class CallsServicesPermissionException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsCallsExceptions {\n  implicit def callsServicesException =\n    (t: Throwable) => CallsServicesException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/calls/impl/CallsServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.calls.impl\n\nimport cards.nine.commons.contentresolver.ContentResolverWrapper\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.services.calls.CallsContentProvider.{allFields, _}\nimport cards.nine.services.calls.{\n  CallsServices,\n  CallsServicesException,\n  CallsServicesPermissionException,\n  ImplicitsCallsExceptions\n}\nimport cards.nine.services.contacts._\nimport monix.eval.Task\nimport cats.syntax.either._\n\nclass CallsServicesImpl(contentResolverWrapper: ContentResolverWrapper)\n    extends CallsServices\n    with ImplicitsCallsExceptions {\n\n  override def getLastCalls =\n    TaskService {\n      Task {\n        Either.catchNonFatal {\n          contentResolverWrapper.fetchAll(\n            uri = Fields.CALL_CONTENT_URI,\n            projection = allFields,\n            orderBy = s\"${Fields.CALL_DATE} desc\")(getListFromCursor(callFromCursor))\n        } leftMap {\n          case e: SecurityException => CallsServicesPermissionException(e.getMessage, Some(e))\n          case e                    => CallsServicesException(e.getMessage, Option(e))\n        }\n      }\n    }\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/connectivity/ConnectivityServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.connectivity\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.NineCardsBluetoothDevice\n\ntrait ConnectivityServices {\n\n  /**\n   * Get the current SSID if it is available\n   *\n   * @return an Option[String] that contains the name of the SSID\n   * @throws WifiServicesException if exist some problem to get the current SSID\n   */\n  def getCurrentSSID(implicit contextSupport: ContextSupport): TaskService[Option[String]]\n\n  /**\n   * Get all configured networks sorted by name. The Wifi must be connected\n   *\n   * @return Seq[String] that contains all SSIDs\n   * @throws WifiServicesException if exist some problem getting the information\n   */\n  def getConfiguredNetworks(implicit contextSupport: ContextSupport): TaskService[Seq[String]]\n\n  /**\n   * Get all paired devices by Bluetooth. The Bluetooth must be connected\n   *\n   * @return Seq[BluetoothDevice] list of devices\n   * @throws BluetoothServicesException if exist some problem getting the information\n   */\n  def getPairedDevices: TaskService[Seq[NineCardsBluetoothDevice]]\n\n  /**\n   * Get all bluetooth connected\n   *\n   * @return Seq[String] list of name of bluetooth\n   * @throws BluetoothServicesException if exist some problem getting the information\n   */\n  def getBluetoothConnected(implicit contextSupport: ContextSupport): TaskService[Set[String]]\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/connectivity/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.connectivity\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class WifiServicesException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsWifiExceptions {\n  implicit def wifiServicesException =\n    (t: Throwable) => WifiServicesException(t.getMessage, Option(t))\n}\n\ncase class BluetoothServicesException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsBluetoothExceptions {\n  implicit def bluetoothServicesException =\n    (t: Throwable) => BluetoothServicesException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/connectivity/impl/ConnectivityServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.connectivity.impl\n\nimport java.util\n\nimport android.bluetooth.{BluetoothAdapter, BluetoothDevice}\nimport android.content.Context\nimport android.net.ConnectivityManager\nimport android.net.wifi.WifiManager\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.models.NineCardsBluetoothDevice\nimport cards.nine.models.types.BluetoothType\nimport cards.nine.services.connectivity._\n\nclass ConnectivityServicesImpl\n    extends ConnectivityServices\n    with ImplicitsWifiExceptions\n    with ImplicitsBluetoothExceptions {\n\n  override def getCurrentSSID(implicit contextSupport: ContextSupport) =\n    TaskService {\n      CatchAll[WifiServicesException] {\n        val connManager = getConnectivityManager\n        val networkInfo = connManager flatMap (manager => Option(manager.getActiveNetworkInfo))\n\n        def nonEmpty(s: String): Boolean = Option(s) match {\n          case Some(string) if string.nonEmpty => true\n          case _                               => false\n        }\n\n        networkInfo match {\n          case Some(n)\n              if n.isConnected &&\n                n.getType == ConnectivityManager.TYPE_WIFI =>\n            val regex = \"((\\\"(.*)\\\")|(.*))\".r\n            Option(n.getExtraInfo) find (_.nonEmpty) flatMap {\n              case regex(_, _, g1, g2) if nonEmpty(g1) => Some(g1)\n              case regex(_, _, g1, g2) if nonEmpty(g2) => Some(g2)\n              case _                                   => None\n            }\n          case _ => None\n        }\n      }\n    }\n\n  override def getConfiguredNetworks(implicit contextSupport: ContextSupport) =\n    TaskService {\n      CatchAll[WifiServicesException] {\n        import scala.collection.JavaConversions._\n        val wifiManager = getWifiManager\n        val networks = wifiManager flatMap (manager =>\n                                              Option(manager.getConfiguredNetworks)) map (_.toList) getOrElse List.empty\n        networks map (_.SSID.replace(\"\\\"\", \"\")) sortWith (_.toLowerCase() < _.toLowerCase())\n      }\n    }\n\n  override def getPairedDevices =\n    TaskService {\n      CatchAll[BluetoothServicesException] {\n        import scala.collection.JavaConversions._\n        getBondedDevices.map { device =>\n          NineCardsBluetoothDevice(\n            name = device.getName,\n            address = device.getAddress,\n            bluetoothType = BluetoothType(device.getBluetoothClass.getMajorDeviceClass))\n        }.toSeq\n      }\n    }\n\n  override def getBluetoothConnected(implicit contextSupport: ContextSupport) =\n    TaskService {\n      CatchAll[BluetoothServicesException] {\n        contextSupport.getBluetoothDevicesConnected\n      }\n    }\n\n  protected def getBondedDevices: util.Set[BluetoothDevice] =\n    BluetoothAdapter.getDefaultAdapter.getBondedDevices\n\n  private[this] def getConnectivityManager(\n      implicit contextSupport: ContextSupport): Option[ConnectivityManager] =\n    contextSupport.context.getSystemService(Context.CONNECTIVITY_SERVICE) match {\n      case conn: ConnectivityManager => Some(conn)\n      case _                         => None\n    }\n\n  private[this] def getWifiManager(implicit contextSupport: ContextSupport): Option[WifiManager] =\n    contextSupport.context.getSystemService(Context.WIFI_SERVICE) match {\n      case manager: WifiManager => Some(manager)\n      case _                    => None\n    }\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/contacts/ContactsContentProvider.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.contacts\n\nimport android.database.Cursor\nimport cards.nine.commons.contentresolver.UriCreator\nimport cards.nine.models.{Contact, ContactEmail, ContactPhone}\nimport cards.nine.models.types._\n\nobject ContactsContentProvider {\n\n  val allFields =\n    Seq(Fields.LOOKUP_KEY, Fields.DISPLAY_NAME, Fields.HAS_PHONE_NUMBER, Fields.STARRED)\n\n  def nameFromCursor(cursor: Cursor): String =\n    cursor.getString(cursor.getColumnIndex(Fields.DISPLAY_NAME))\n\n  def contactFromCursor(uriCreator: UriCreator, cursor: Cursor) =\n    readContact(\n      uriCreator = uriCreator,\n      cursor = cursor,\n      lookupKeyColumn = Fields.LOOKUP_KEY,\n      nameColumn = Fields.DISPLAY_NAME,\n      hasPhoneColumn = Fields.HAS_PHONE_NUMBER,\n      starredColumn = Fields.STARRED)\n\n  val allEmailContactFields = Seq(\n    Fields.EMAIL_LOOKUP_KEY,\n    Fields.EMAIL_DISPLAY_NAME,\n    Fields.EMAIL_HAS_PHONE_NUMBER,\n    Fields.EMAIL_STARRED)\n\n  val allEmailFields = Seq(Fields.EMAIL_LOOKUP_KEY, Fields.EMAIL_TYPE, Fields.EMAIL_ADDRESS)\n\n  def contactFromEmailCursor(uriCreator: UriCreator, cursor: Cursor) =\n    readContact(\n      uriCreator = uriCreator,\n      cursor = cursor,\n      lookupKeyColumn = Fields.EMAIL_LOOKUP_KEY,\n      nameColumn = Fields.EMAIL_DISPLAY_NAME,\n      hasPhoneColumn = Fields.EMAIL_HAS_PHONE_NUMBER,\n      starredColumn = Fields.EMAIL_STARRED)\n\n  def emailFromCursor(cursor: Cursor) =\n    ContactEmail(\n      address = cursor.getString(cursor.getColumnIndex(Fields.EMAIL_ADDRESS)),\n      category = parseEmailType(cursor.getInt(cursor.getColumnIndex(Fields.EMAIL_TYPE))))\n\n  def lookupKeyAndEmailFromCursor(cursor: Cursor): (String, ContactEmail) =\n    (cursor.getString(cursor.getColumnIndex(Fields.EMAIL_LOOKUP_KEY)),\n     ContactEmail(\n       address = cursor.getString(cursor.getColumnIndex(Fields.EMAIL_ADDRESS)),\n       category = parseEmailType(cursor.getInt(cursor.getColumnIndex(Fields.EMAIL_TYPE)))))\n\n  def parseEmailType(phoneType: Int): EmailCategory =\n    phoneType match {\n      case Fields.EMAIL_TYPE_HOME => EmailHome\n      case Fields.EMAIL_TYPE_WORK => EmailWork\n      case _                      => EmailOther\n    }\n\n  val allPhoneContactFields = Seq(\n    Fields.PHONE_LOOKUP_KEY,\n    Fields.PHONE_DISPLAY_NAME,\n    Fields.PHONE_HAS_PHONE_NUMBER,\n    Fields.PHONE_STARRED)\n\n  val allPhoneFields = Seq(\n    Fields.PHONE_LOOKUP_KEY,\n    Fields.PHONE_TYPE,\n    Fields.PHONE_NUMBER,\n    Fields.PHONE_CUSTOM_RINGTONE)\n\n  def contactFromPhoneCursor(uriCreator: UriCreator, cursor: Cursor) =\n    readContact(\n      uriCreator = uriCreator,\n      cursor = cursor,\n      lookupKeyColumn = Fields.PHONE_LOOKUP_KEY,\n      nameColumn = Fields.PHONE_DISPLAY_NAME,\n      hasPhoneColumn = Fields.PHONE_HAS_PHONE_NUMBER,\n      starredColumn = Fields.PHONE_STARRED)\n\n  def phoneFromCursor(cursor: Cursor) =\n    ContactPhone(\n      number = cursor.getString(cursor.getColumnIndex(Fields.PHONE_NUMBER)),\n      category = parsePhoneType(cursor.getInt(cursor.getColumnIndex(Fields.PHONE_TYPE))))\n\n  def lookupKeyAndPhoneFromCursor(cursor: Cursor): (String, ContactPhone) =\n    (cursor.getString(cursor.getColumnIndex(Fields.PHONE_LOOKUP_KEY)),\n     ContactPhone(\n       number = cursor.getString(cursor.getColumnIndex(Fields.PHONE_NUMBER)),\n       category = parsePhoneType(cursor.getInt(cursor.getColumnIndex(Fields.PHONE_TYPE)))))\n\n  def parsePhoneType(phoneType: Int): PhoneCategory =\n    phoneType match {\n      case Fields.PHONE_TYPE_HOME     => PhoneHome\n      case Fields.PHONE_TYPE_WORK     => PhoneWork\n      case Fields.PHONE_TYPE_MOBILE   => PhoneMobile\n      case Fields.PHONE_TYPE_MAIN     => PhoneMain\n      case Fields.PHONE_TYPE_FAX_WORK => PhoneFaxWork\n      case Fields.PHONE_TYPE_FAX_HOME => PhoneFaxHome\n      case Fields.PHONE_TYPE_PAGER    => PhonePager\n      case _                          => PhoneOther\n    }\n\n  private[this] def readContact(\n      uriCreator: UriCreator,\n      cursor: Cursor,\n      lookupKeyColumn: String,\n      nameColumn: String,\n      hasPhoneColumn: String,\n      starredColumn: String) = {\n    val lookupKey = cursor.getString(cursor.getColumnIndex(lookupKeyColumn))\n    Contact(\n      lookupKey = lookupKey,\n      photoUri = uriCreator.withAppendedPath(Fields.PHOTO_URI, lookupKey).toString,\n      name = cursor.getString(cursor.getColumnIndex(nameColumn)),\n      hasPhone = cursor.getInt(cursor.getColumnIndex(hasPhoneColumn)) > 0,\n      favorite = cursor.getInt(cursor.getColumnIndex(starredColumn)) > 0)\n  }\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/contacts/ContactsServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.contacts\n\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.{Contact, IterableCursor, TermCounter}\n\ntrait ContactsServices {\n\n  /**\n   * Get contacts sort by name. The info field is not filled\n   *\n   * @return the Seq[cards.nine.models.Contact] contains information about contacts\n   * @throws ContactsServicePermissionException if the permission for read contacts hasn't been granted\n   * @throws ContactsServiceException if exist some problem accessing to contact provider\n   */\n  def getContacts: TaskService[Seq[Contact]]\n\n  /**\n   * Returns the number of times the first letter of a contact is repeated alphabetically\n   *\n   * @return the Seq[cards.nine.models.TermCounter] contains information about the times is repeated a contacts\n   * @throws ContactsServicePermissionException if the permission for read contacts hasn't been granted\n   * @throws ContactsServiceException if exist some problem accessing to contact provider\n   */\n  def getAlphabeticalCounterContacts: TaskService[Seq[TermCounter]]\n\n  /**\n   * Get iterable contacts sort by name. The info field is not filled\n   *\n   * @return the IterableCursorSeq[cards.nine.models.Contact] contains information about contacts\n   * @throws ContactsServicePermissionException if the permission for read contacts hasn't been granted\n   * @throws ContactsServiceException if exist some problem accessing to contact provider\n   */\n  def getIterableContacts: TaskService[IterableCursor[Contact]]\n\n  /**\n   * Get iterable contacts by keyword sort by name. The info field is not filled\n   *\n   * @return the IterableCursorSeq[cards.nine.models.Contact] contains information about contacts\n   * @throws ContactsServicePermissionException if the permission for read contacts hasn't been granted\n   * @throws ContactsServiceException if exist some problem accessing to contact provider\n   */\n  def getIterableContactsByKeyword(keyword: String): TaskService[IterableCursor[Contact]]\n\n  /**\n   * Return contact by email if exist. The info field is not filled\n   *\n   * @return the Option[cards.nine.models.Contact] contains\n   *         information about contact\n   * @throws ContactsServicePermissionException if the permission for read contacts hasn't been granted\n   * @throws ContactsServiceException if exist some problem accessing to contact provider\n   */\n  def fetchContactByEmail(email: String): TaskService[Option[Contact]]\n\n  /**\n   * Return contact by phone number if exist. The info field is not filled\n   *\n   * @return the Option[cards.nine.models.Contact] contains information about contact\n   * @throws ContactsServicePermissionException if the permission for read contacts hasn't been granted\n   * @throws ContactsServiceException if exist some problem accessing to contact provider\n   */\n  def fetchContactByPhoneNumber(phoneNumber: String): TaskService[Option[Contact]]\n\n  /**\n   * Return contact by lookup key. The info field is filled\n   *\n   * @return the cards.nine.models.Contact contains information about contacts\n   * @throws ContactsServicePermissionException if the permission for read contacts hasn't been granted\n   * @throws ContactsServiceException if exist some problem accessing to contact provider\n   * @throws ContactNotFoundException if the lookup key doesn't exits\n   */\n  def findContactByLookupKey(lookupKey: String): TaskService[Contact]\n\n  /**\n   * Populate the info field in every contact\n   *\n   * @return sequence of the cards.nine.models.Contact\n   * @throws ContactsServicePermissionException if the permission for read contacts hasn't been granted\n   * @throws ContactsServiceException if exist some problem accessing to contact provider\n   * @throws ContactNotFoundException if any contact doesn't exits\n   */\n  def populateContactInfo(contacts: Seq[Contact]): TaskService[Seq[Contact]]\n\n  /**\n   * Return favorite contacts. The info field is not filled\n   *\n   * @return the Seq[cards.nine.models.Contact] contains information about contacts\n   * @throws ContactsServicePermissionException if the permission for read contacts hasn't been granted\n   * @throws ContactsServiceException if exist some problem accessing to contact provider\n   */\n  def getFavoriteContacts: TaskService[Seq[Contact]]\n\n  /**\n   * Return iterable favorite contacts. The info field is not filled\n   *\n   * @return the IterableCursor[cards.nine.models.Contact] contains information about contacts\n   * @throws ContactsServicePermissionException if the permission for read contacts hasn't been granted\n   * @throws ContactsServiceException if exist some problem accessing to contact provider\n   */\n  def getIterableFavoriteContacts: TaskService[IterableCursor[Contact]]\n\n  /**\n   * Return contacts with phone number. The info field is not filled\n   *\n   * @return the Seq[cards.nine.models.Contact] contains information about contacts\n   * @throws ContactsServicePermissionException if the permission for read contacts hasn't been granted\n   * @throws ContactsServiceException if exist some problem accessing to contact provider\n   */\n  def getContactsWithPhone: TaskService[Seq[Contact]]\n\n  /**\n   * Return iterable contacts with phone number. The info field is not filled\n   *\n   * @return the IterableCursor[cards.nine.models.Contact] contains information about contacts\n   * @throws ContactsServicePermissionException if the permission for read contacts hasn't been granted\n   * @throws ContactsServiceException if exist some problem accessing to contact provider\n   */\n  def getIterableContactsWithPhone: TaskService[IterableCursor[Contact]]\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/contacts/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.contacts\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class ContactsServiceException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class ContactNotFoundException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class ContactsServicePermissionException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/contacts/impl/ContactsServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.contacts.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapper, UriCreator}\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.IterableCursor._\nimport cards.nine.models._\nimport cards.nine.services.contacts.ContactsContentProvider.{allFields, _}\nimport cards.nine.services.contacts._\nimport cats.syntax.either._\nimport monix.eval.Task\n\nclass ContactsServicesImpl(\n    contentResolverWrapper: ContentResolverWrapper,\n    uriCreator: UriCreator = new UriCreator)\n    extends ContactsServices {\n\n  val abc = \"ABCDEFGHIJKLMNÑOPQRSTUVWXYZ\"\n\n  val wildcard = \"#\"\n\n  override def getContacts =\n    catchMapPermission {\n      contentResolverWrapper.fetchAll(\n        uri = Fields.CONTENT_URI,\n        projection = allFields,\n        where = Fields.ALL_CONTACTS_SELECTION,\n        orderBy = Fields.CONTACTS_ORDER_BY_ASC)(\n        getListFromCursor(contactFromCursor(uriCreator, _)))\n    }\n\n  override def getAlphabeticalCounterContacts =\n    catchMapPermission {\n      val iterator = getNamesAlphabetically\n      iterator.foldLeft(Seq.empty[TermCounter]) { (acc, name) =>\n        val term = name.substring(0, 1).toUpperCase match {\n          case t if abc.contains(t) => t\n          case _                    => wildcard\n        }\n        val lastWithSameTerm = acc.lastOption flatMap {\n          case last if last.term == term => Some(last)\n          case _                         => None\n        }\n        lastWithSameTerm map { c =>\n          acc.dropRight(1) :+ c.copy(count = c.count + 1)\n        } getOrElse acc :+ TermCounter(term, 1)\n      }\n    }\n\n  override def getIterableContacts =\n    catchMapPermission {\n      contentResolverWrapper\n        .getCursor(\n          uri = Fields.CONTENT_URI,\n          projection = allFields,\n          where = Fields.ALL_CONTACTS_SELECTION,\n          orderBy = Fields.CONTACTS_ORDER_BY_ASC)\n        .toIterator(contactFromCursor(uriCreator, _))\n    }\n\n  override def getIterableContactsByKeyword(keyword: String) =\n    catchMapPermission {\n      contentResolverWrapper\n        .getCursor(\n          uri = Fields.CONTENT_URI,\n          projection = allFields,\n          where = Fields.CONTACTS_BY_KEYWORD_SELECTION,\n          whereParams = Seq(s\"%$keyword%\"),\n          orderBy = Fields.CONTACTS_ORDER_BY_ASC)\n        .toIterator(contactFromCursor(uriCreator, _))\n    }\n\n  override def fetchContactByEmail(email: String) =\n    catchMapPermission {\n      contentResolverWrapper.fetch(\n        uri = Fields.EMAIL_CONTENT_URI,\n        projection = allEmailContactFields,\n        where = Fields.EMAIL_SELECTION,\n        whereParams = Seq(email))(getEntityFromCursor(contactFromEmailCursor(uriCreator, _)))\n    }\n\n  override def fetchContactByPhoneNumber(phoneNumber: String) =\n    catchMapPermission {\n      contentResolverWrapper.fetch(\n        uri = uriCreator.withAppendedPath(Fields.PHONE_LOOKUP_URI, phoneNumber),\n        projection = allPhoneContactFields)(\n        getEntityFromCursor(contactFromPhoneCursor(uriCreator, _)))\n    }\n\n  override def findContactByLookupKey(lookupKey: String) = {\n\n    val fetchContacts = () =>\n      contentResolverWrapper.fetchAll(\n        uri = Fields.CONTENT_URI,\n        projection = allFields,\n        where = Fields.LOOKUP_SELECTION,\n        whereParams = Seq(lookupKey))(getListFromCursor(contactFromCursor(uriCreator, _)))\n\n    populateContacts(fetchContacts).resolveRight {\n      _.headOption match {\n        case Some(v) => Right(v)\n        case None    => Left(ContactNotFoundException(s\"The lookupKey $lookupKey can't be found\"))\n      }\n    }\n  }\n\n  override def populateContactInfo(contacts: Seq[Contact]) = populateContacts(() => contacts)\n\n  private[this] def populateContacts(\n      fetchContacts: () => Seq[Contact]): TaskService[Seq[Contact]] = {\n\n    def mapValues[T](seq: Seq[(String, T)]): Map[String, Seq[T]] =\n      seq.groupBy(_._1).mapValues(_.map(_._2))\n\n    def emailAndPhones(lookupKeys: Seq[String]): (Map[String, Seq[ContactEmail]],\n                                                  Map[String, Seq[ContactPhone]]) = {\n\n      val inArgs = lookupKeys.map(key => s\"'$key'\").mkString(\",\")\n\n      (mapValues(\n         contentResolverWrapper.fetchAll(\n           uri = Fields.EMAIL_CONTENT_URI,\n           projection = allEmailFields,\n           where = s\"${Fields.EMAIL_CONTACT_SELECTION} ($inArgs)\")(\n           getListFromCursor(lookupKeyAndEmailFromCursor))),\n       mapValues(\n         contentResolverWrapper.fetchAll(\n           uri = Fields.PHONE_CONTENT_URI,\n           projection = allPhoneFields,\n           where = s\"${Fields.PHONE_CONTACT_SELECTION} ($inArgs)\")(\n           getListFromCursor(lookupKeyAndPhoneFromCursor))))\n    }\n\n    catchMapPermission {\n      val contacts         = fetchContacts()\n      val (emails, phones) = emailAndPhones(contacts.map(_.lookupKey))\n      contacts map { contact =>\n        (emails.getOrElse(contact.lookupKey, Seq.empty),\n         phones.getOrElse(contact.lookupKey, Seq.empty)) match {\n          case (contactEmails, contactPhones)\n              if contactEmails.nonEmpty || contactPhones.nonEmpty =>\n            contact.copy(info = Some(ContactInfo(contactEmails, contactPhones)))\n          case _ =>\n            contact\n        }\n      }\n    }\n  }\n\n  override def getFavoriteContacts =\n    catchMapPermission {\n      contentResolverWrapper.fetchAll(\n        uri = Fields.CONTENT_URI,\n        projection = allFields,\n        where = Fields.STARRED_SELECTION,\n        orderBy = Fields.CONTACTS_ORDER_BY_ASC)(\n        getListFromCursor(contactFromCursor(uriCreator, _)))\n    }\n\n  override def getIterableFavoriteContacts =\n    catchMapPermission {\n      contentResolverWrapper\n        .getCursor(\n          uri = Fields.CONTENT_URI,\n          projection = allFields,\n          where = Fields.STARRED_SELECTION,\n          orderBy = Fields.CONTACTS_ORDER_BY_ASC)\n        .toIterator(contactFromCursor(uriCreator, _))\n    }\n\n  override def getContactsWithPhone =\n    catchMapPermission {\n      contentResolverWrapper.fetchAll(\n        uri = Fields.CONTENT_URI,\n        projection = allFields,\n        where = Fields.HAS_PHONE_NUMBER_SELECTION,\n        orderBy = Fields.CONTACTS_ORDER_BY_ASC)(\n        getListFromCursor(contactFromCursor(uriCreator, _)))\n    }\n\n  override def getIterableContactsWithPhone =\n    catchMapPermission {\n      contentResolverWrapper\n        .getCursor(\n          uri = Fields.CONTENT_URI,\n          projection = allFields,\n          where = Fields.HAS_PHONE_NUMBER_SELECTION,\n          orderBy = Fields.CONTACTS_ORDER_BY_ASC)\n        .toIterator(contactFromCursor(uriCreator, _))\n    }\n\n  protected def getNamesAlphabetically: Seq[String] = {\n    getListFromCursor(nameFromCursor)(\n      contentResolverWrapper.getCursor(\n        uri = Fields.CONTENT_URI,\n        projection = Seq(Fields.DISPLAY_NAME),\n        where = Fields.ALL_CONTACTS_SELECTION,\n        orderBy = Fields.CONTACTS_ORDER_BY_ASC))\n  }\n\n  def catchMapPermission[V](f: => V) =\n    TaskService {\n      Task {\n        Either.catchNonFatal(f) leftMap {\n          case e: SecurityException => ContactsServicePermissionException(e.getMessage, Some(e))\n          case e                    => ContactsServiceException(e.getMessage, Some(e))\n        }\n      }\n    }\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/image/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.image\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class FileException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class BitmapTransformationException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsImageExceptions {\n\n  implicit def fileException = (t: Throwable) => FileException(t.getMessage, Option(t))\n\n  implicit def bitmapTransformationException =\n    (t: Throwable) => BitmapTransformationException(t.getMessage, Option(t))\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/image/ImageServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.image\n\nimport android.content.Intent.ShortcutIconResource\nimport android.graphics.Bitmap\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.BitmapPath\n\ntrait ImageServices {\n\n  /**\n   * Write a compressed version of the bitmap pass by parameter and return the path\n   * @param bitmap the bitmap to save\n   * @param width the width of the bitmap to save\n   * @param height the height of the bitmap to save\n   * @return the cards.nine.services.image.SaveBitmapPath contains\n   *         path where the file was stored\n   * @throws FileException if exist some problem storing bitmap\n   */\n  def saveBitmap(bitmap: Bitmap, width: Option[Int], height: Option[Int])(\n      implicit contextSupport: ContextSupport): TaskService[BitmapPath]\n\n  /**\n   * Decode a Bitmap from a ShortcutIconResource\n   * @param resource the ShortcutIconResource\n   * @return the decoded Bitmap\n   */\n  def decodeShortcutIconResource(resource: ShortcutIconResource)(\n      implicit context: ContextSupport): TaskService[Bitmap]\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/image/impl/ImageServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.image.impl\n\nimport android.content.Intent.ShortcutIconResource\nimport android.graphics.Bitmap\nimport android.media.ThumbnailUtils\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.BitmapPath\nimport cards.nine.services.image._\n\nclass ImageServicesImpl(imageServicesTasks: ImageServicesTasks = ImageServicesTasks)\n    extends ImageServices {\n\n  override def saveBitmap(bitmap: Bitmap, maybeWidth: Option[Int], maybeHeight: Option[Int])(\n      implicit contextSupport: ContextSupport) = {\n\n    val uniqueName = com.gilt.timeuuid.TimeUuid().toString\n\n    def resizeBitmap = (maybeWidth, maybeHeight) match {\n      case (Some(width), Some(height)) =>\n        ThumbnailUtils\n          .extractThumbnail(bitmap, width, height, ThumbnailUtils.OPTIONS_RECYCLE_INPUT)\n      case _ => bitmap\n    }\n\n    for {\n      file <- imageServicesTasks.getPathByName(uniqueName)\n      _    <- imageServicesTasks.saveBitmap(file, resizeBitmap)\n    } yield BitmapPath(uniqueName, file.getAbsolutePath)\n  }\n\n  override def decodeShortcutIconResource(resource: ShortcutIconResource)(\n      implicit context: ContextSupport): TaskService[Bitmap] =\n    imageServicesTasks.getBitmapFromShortcutIconResource(resource)\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/image/impl/ImageServicesTasks.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.image.impl\n\nimport java.io.{File, FileOutputStream, InputStream}\nimport java.net.URL\n\nimport android.content.Intent.ShortcutIconResource\nimport android.content.res.Resources\nimport android.graphics._\nimport cards.nine.commons._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.services.image._\nimport cards.nine.services.utils.ResourceUtils\n\ntrait ImageServicesTasks extends ImplicitsImageExceptions {\n\n  val noDensity = 0\n\n  val resourceUtils = new ResourceUtils\n\n  def getPathByName(name: String)(implicit context: ContextSupport): TaskService[File] =\n    TaskService {\n      CatchAll[FileException] {\n        new File(resourceUtils.getPath(name))\n      }\n    }\n\n  def getBitmapFromURL(uri: String): TaskService[Bitmap] = TaskService {\n    CatchAll[BitmapTransformationException] {\n      createInputStream(uri) match {\n        case is: InputStream => createBitmapByInputStream(is)\n        case _ =>\n          throw BitmapTransformationException(\n            s\"Unexpected error while fetching content from uri: $uri\")\n      }\n    }\n  }\n\n  def getBitmapFromShortcutIconResource(resource: ShortcutIconResource)(\n      implicit context: ContextSupport): TaskService[Bitmap] = TaskService {\n    CatchAll[BitmapTransformationException] {\n      val resources = context.getPackageManager.getResourcesForApplication(resource.packageName)\n      val id        = resources.getIdentifier(resource.resourceName, javaNull, javaNull)\n      Option(createBitmapByResource(resources, id)) match {\n        case Some(bitmap) => bitmap\n        case _ =>\n          throw BitmapTransformationException(s\"Received null when decoding resource: $resource\")\n      }\n    }\n  }\n\n  def saveBitmap(file: File, bitmap: Bitmap): TaskService[Unit] = TaskService {\n    CatchAll[FileException] {\n      val out = createFileOutputStream(file)\n      bitmap.compress(Bitmap.CompressFormat.PNG, 90, out)\n      out.flush()\n      out.close()\n    }\n  }\n\n  protected def createInputStream(uri: String) = new URL(uri).getContent\n\n  protected def createBitmapByInputStream(is: InputStream) = BitmapFactory.decodeStream(is)\n\n  protected def createFileOutputStream(file: File): FileOutputStream = new FileOutputStream(file)\n\n  protected def createBitmapByResource(resources: Resources, id: Int) =\n    BitmapFactory.decodeResource(resources, id)\n\n}\n\nobject ImageServicesTasks extends ImageServicesTasks\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/intents/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.intents\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class IntentLauncherServicesException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class IntentLauncherServicesPermissionException(\n    message: String,\n    cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/intents/LauncherIntentServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.intents\n\nimport android.content.Intent\nimport cards.nine.commons.contexts.ActivityContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.IntentAction\n\ntrait LauncherIntentServices {\n\n  /**\n   * This method try to execute an intent based on the provided action\n   * @param intentAction specifies the intent to be executed\n   * @return a TaskService[Unit] that will contain an Unit if the intent has been executed successfully\n   *         an IntentLauncherServicesPermissionException if there are insufficient permissions to execute the intent or\n   *         an IntentLauncherServicesException if the service can't access to the activity or the execution throw a different exception\n   */\n  def launchIntentAction(intentAction: IntentAction)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * This method try to execute directly an intent\n   * @param intent the intent to be executed\n   * @return a TaskService[Unit] that will contain an Unit if the intent has been executed successfully\n   *         an IntentLauncherServicesPermissionException if there are insufficient permissions to execute the intent or\n   *         an IntentLauncherServicesException if the service can't access to the activity or the execution throw a different exception\n   */\n  def launchIntent(intent: Intent)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit]\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/intents/impl/IntentCreator.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.intents.impl\n\nimport android.app.SearchManager\nimport android.content.{ComponentName, Intent}\nimport android.net.Uri\nimport android.provider.ContactsContract\nimport android.speech.RecognizerIntent\nimport cards.nine.commons._\nimport cards.nine.commons.contexts.ActivityContextSupport\n\nclass IntentCreator {\n\n  val emailType = \"message/rfc822\"\n\n  val shareType = \"text/plain\"\n\n  val searchPackageName = \"com.google.android.googlequicksearchbox\"\n  val searchClassName   = \"com.google.android.googlequicksearchbox.SearchActivity\"\n\n  val googleWeatherUri     = \"dynact://velour/weather/ProxyActivity\"\n  val googleWeatherPackage = \"com.google.android.googlequicksearchbox\"\n  val googleWeatherClass   = \"com.google.android.apps.gsa.velour.DynamicActivityTrampoline\"\n\n  val googlePlayStorePackage = \"com.android.vending\"\n\n  def createAppIntent(packageName: String, className: String): Intent = {\n    val intent = new Intent(Intent.ACTION_MAIN)\n    intent.addCategory(Intent.CATEGORY_LAUNCHER)\n    intent.setComponent(new ComponentName(packageName, className))\n    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n    intent\n  }\n\n  def createAppLaunchIntent(packageName: String)(\n      implicit activityContext: ActivityContextSupport): Intent =\n    activityContext.getPackageManager.getLaunchIntentForPackage(packageName)\n\n  def createAppSettingsIntent(packageName: String): Intent =\n    createSettingsIntent(Some(packageName))\n\n  def createGlobalSettingsIntent(): Intent =\n    createSettingsIntent()\n\n  private[this] def createSettingsIntent(maybePackageName: Option[String] = None): Intent = {\n    val intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS)\n    intent.addCategory(Intent.CATEGORY_DEFAULT)\n    maybePackageName foreach (packageName => intent.setData(Uri.parse(s\"package:$packageName\")))\n    intent\n  }\n\n  def createAppUninstallIntent(packageName: String): Intent =\n    new Intent(Intent.ACTION_UNINSTALL_PACKAGE, Uri.parse(s\"package:$packageName\"))\n\n  def createAppGooglePlayIntent(googlePlayUrl: String, packageName: String): Intent = {\n    val intent = new Intent(Intent.ACTION_VIEW, Uri.parse(s\"$googlePlayUrl?id=$packageName\"))\n    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n    intent\n  }\n\n  def createPhoneSmsIntent(phoneNumber: String): Intent =\n    new Intent(Intent.ACTION_VIEW, Uri.fromParts(\"sms\", phoneNumber, javaNull))\n\n  def createPhoneCallIntent(phoneNumber: String): Intent =\n    createCallIntent(Option(phoneNumber), Intent.ACTION_CALL)\n\n  def createPhoneDialIntent(maybePhoneNumber: Option[String]): Intent =\n    createCallIntent(maybePhoneNumber, Intent.ACTION_DIAL)\n\n  private[this] def createCallIntent(maybePhoneNumber: Option[String], action: String): Intent = {\n    val intent = new Intent(action)\n    maybePhoneNumber foreach (phoneNumber => intent.setData(Uri.parse(s\"tel:$phoneNumber\")))\n    intent\n  }\n\n  def createEmailIntent(email: String, titleDialog: String): Intent = {\n    val newIntent = new Intent(Intent.ACTION_SEND)\n    newIntent.setType(emailType)\n    newIntent.putExtra(Intent.EXTRA_EMAIL, Array(email))\n    Intent.createChooser(newIntent, titleDialog)\n  }\n\n  def createContactIntent(lookupKey: String): Intent = {\n    val contactUri =\n      ContactsContract.Contacts.CONTENT_LOOKUP_URI.buildUpon().appendPath(lookupKey).build()\n    new Intent(Intent.ACTION_VIEW, contactUri)\n  }\n\n  def createShareIntent(text: String, titleDialog: String): Intent = {\n    val intent = new Intent()\n    intent.setAction(Intent.ACTION_SEND)\n    intent.putExtra(Intent.EXTRA_TEXT, text)\n    intent.setType(shareType)\n    Intent.createChooser(intent, titleDialog)\n  }\n\n  def createSearchGlobalIntent(): Intent = {\n    val intent        = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH)\n    val componentName = new ComponentName(searchPackageName, searchClassName)\n    intent.setComponent(componentName)\n    intent\n  }\n\n  def createSearchWebIntent(): Intent = new Intent(Intent.ACTION_WEB_SEARCH)\n\n  def createSearchVoiceIntent(): Intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH)\n\n  def createGoogleWeatherIntent(): Intent = {\n    val intent = new Intent(Intent.ACTION_VIEW)\n    intent.setData(Uri.parse(googleWeatherUri))\n    intent.setClassName(googleWeatherPackage, googleWeatherClass)\n    intent\n  }\n\n  def createGooglePlayStoreIntent()(implicit activityContext: ActivityContextSupport): Intent =\n    createAppLaunchIntent(googlePlayStorePackage)\n\n  def createUrlViewIntent(url: String): Intent = {\n    val intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url))\n    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n    intent\n  }\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/intents/impl/LauncherIntentServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.intents.impl\n\nimport android.app.Activity\nimport android.content.Intent\nimport cards.nine.commons.contexts.ActivityContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService.{NineCardException, TaskService}\nimport cards.nine.models._\nimport cards.nine.services.intents.{\n  IntentLauncherServicesException,\n  IntentLauncherServicesPermissionException,\n  LauncherIntentServices\n}\nimport cats.syntax.either._\nimport monix.eval.Task\n\nclass LauncherIntentServicesImpl extends LauncherIntentServices {\n\n  val intentCreator = new IntentCreator\n\n  override def launchIntentAction(intentAction: IntentAction)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit] = {\n\n    import intentCreator._\n    val intent = intentAction match {\n      case AppAction(packageName, className) =>\n        createAppIntent(packageName, className)\n      case AppGooglePlayAction(googlePlayUrl, packageName) =>\n        createAppGooglePlayIntent(googlePlayUrl, packageName)\n      case AppLauncherAction(packageName) =>\n        createAppLaunchIntent(packageName)\n      case AppSettingsAction(packageName) =>\n        createAppSettingsIntent(packageName)\n      case AppUninstallAction(packageName) =>\n        createAppUninstallIntent(packageName)\n      case ContactAction(lookupKey) =>\n        createContactIntent(lookupKey)\n      case EmailAction(email, titleDialog) =>\n        createEmailIntent(email, titleDialog)\n      case GlobalSettingsAction =>\n        createGlobalSettingsIntent()\n      case GooglePlayStoreAction =>\n        createGooglePlayStoreIntent()\n      case GoogleWeatherAction =>\n        createGoogleWeatherIntent()\n      case PhoneCallAction(phoneNumber) =>\n        createPhoneCallIntent(phoneNumber)\n      case PhoneDialAction(maybePhoneNumber) =>\n        createPhoneDialIntent(maybePhoneNumber)\n      case PhoneSmsAction(phoneNumber) =>\n        createPhoneSmsIntent(phoneNumber)\n      case SearchGlobalAction =>\n        createSearchGlobalIntent()\n      case SearchVoiceAction =>\n        createSearchVoiceIntent()\n      case SearchWebAction =>\n        createSearchWebIntent()\n      case ShareAction(text, titleDialog) =>\n        createShareIntent(text, titleDialog)\n      case UrlAction(url) =>\n        createUrlViewIntent(url)\n    }\n    launchIntent(intent)\n  }\n\n  override def launchIntent(intent: Intent)(\n      implicit activityContext: ActivityContextSupport): TaskService[Unit] =\n    TaskService {\n      Task {\n        withActivity { activity =>\n          Either.catchNonFatal {\n            activity.startActivity(intent)\n          } leftMap {\n            case e: SecurityException =>\n              IntentLauncherServicesPermissionException(e.getMessage, Some(e))\n            case e => IntentLauncherServicesException(e.getMessage, Some(e))\n          }\n        }\n      }\n    }\n\n  def withActivity(f: (Activity) => Either[NineCardException, Unit])(\n      implicit activityContext: ActivityContextSupport): Either[NineCardException, Unit] =\n    activityContext.getActivity match {\n      case Some(activity) => f(activity)\n      case None           => Left(IntentLauncherServicesException(\"Activity not available\", None))\n    }\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/permissions/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.permissions\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class PermissionsServicesException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsPermissionsServicesExceptions {\n\n  implicit def permissionsServicesExceptionConverter =\n    (t: Throwable) => PermissionsServicesException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/permissions/PermissionsServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.permissions\n\nimport cards.nine.commons.contexts.{ActivityContextSupport, ContextSupport}\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.types.PermissionStatus\n\ntrait PermissionsServices {\n\n  /**\n   * Verifies the status of some permissions\n   *\n   * @param permissions the permissions\n   * @return a map with the status of each permission\n   * @throws PermissionsServicesException if there was some problem verifying the permission\n   */\n  def checkPermissions(permissions: Seq[String])(\n      implicit contextSupport: ContextSupport): TaskService[Map[String, PermissionStatus]]\n\n  /**\n   * Check if we could show a request for the specified permissions\n   *\n   * @param permissions the permissions\n   * @return a map with true or false indicating if we should show the request\n   * @throws PermissionsServicesException if there was some problem verifying the permission\n   */\n  def shouldShowRequestPermissions(permissions: Seq[String])(\n      implicit contextSupport: ActivityContextSupport): TaskService[Map[String, Boolean]]\n\n  /**\n   * Try to ask for permissions\n   *\n   * @param requestCode the request code\n   * @param permissions the permissions\n   * @throws PermissionsServicesException if there was some problem trying to request the permissions\n   */\n  def requestPermissions(requestCode: Int, permissions: Seq[String])(\n      implicit contextSupport: ActivityContextSupport): TaskService[Unit]\n\n  /**\n   * Parses the permission request response\n   *\n   * @param permissions the permissions names\n   * @param grantResults the status codes\n   * @return a map with the status of each permission\n   * @throws PermissionsServicesException if there was some problem parsing the permission statuses\n   */\n  def readPermissionsRequestResult(\n      permissions: Seq[String],\n      grantResults: Seq[Int]): TaskService[Map[String, PermissionStatus]]\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class AndroidIdNotFoundException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class InstallationNotFoundException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class PersistenceServiceException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ncase class UserNotFoundException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsPersistenceServiceExceptions {\n\n  implicit def androidIdNotFoundException =\n    (t: Throwable) => AndroidIdNotFoundException(t.getMessage, Option(t))\n\n  implicit def installationNotFoundException =\n    (t: Throwable) => InstallationNotFoundException(t.getMessage, Option(t))\n\n  implicit def persistenceServiceException =\n    (t: Throwable) => PersistenceServiceException(t.getMessage, Option(t))\n\n  implicit def userNotFoundException =\n    (t: Throwable) => UserNotFoundException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/PersistenceServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models._\nimport cards.nine.models.types.{FetchAppOrder, NineCardsMoment}\n\ntrait PersistenceServices {\n\n  /**\n   * Obtains all the apps from the repository\n   *\n   * @param orderBy   indicates the field to order by\n   * @param ascending indicates if it will be in ascending order or not\n   * @return the Seq[cards.nine.models.App]\n   * @throws PersistenceServiceException if exist some problem obtaining the app\n   */\n  def fetchApps(orderBy: FetchAppOrder, ascending: Boolean = true): TaskService[Seq[Application]]\n\n  /**\n   * Obtains iterable of apps from the repository\n   *\n   * @param orderBy   indicates the field to order by\n   * @param ascending indicates if it will be in ascending order or not\n   * @return the cards.nine.models.IterableApps\n   * @throws PersistenceServiceException if exist some problem obtaining the app\n   */\n  def fetchIterableApps(\n      orderBy: FetchAppOrder,\n      ascending: Boolean = true): TaskService[IterableApplicationData]\n\n  /**\n   * Obtains iterable of apps by keywords from the repository\n   *\n   * @param keyword keyword for search\n   * @param orderBy indicates the field to order by\n   * @param ascending indicates if it will be in ascending order or not\n   * @return the cards.nine.models.IterableApps\n   * @throws PersistenceServiceException if exist some problem obtaining the app\n   */\n  def fetchIterableAppsByKeyword(\n      keyword: String,\n      orderBy: FetchAppOrder,\n      ascending: Boolean = true): TaskService[IterableApplicationData]\n\n  /**\n   * Obtains all the apps by category from the repository\n   *\n   * @param category category for search\n   * @param orderBy indicates the field to order by\n   * @param ascending indicates if it will be in ascending order or not\n   * @return the Seq[cards.nine.models.App]\n   * @throws PersistenceServiceException if exist some problem obtaining the app\n   */\n  def fetchAppsByCategory(\n      category: String,\n      orderBy: FetchAppOrder,\n      ascending: Boolean = true): TaskService[Seq[Application]]\n\n  /**\n   * Obtains iterable of apps by category from the repository\n   *\n   * @param category category for search\n   * @param orderBy indicates the field to order by\n   * @param ascending indicates if it will be in ascending order or not\n   * @return the cards.nine.models.IterableApps\n   * @throws PersistenceServiceException if exist some problem obtaining the apps\n   */\n  def fetchIterableAppsByCategory(\n      category: String,\n      orderBy: FetchAppOrder,\n      ascending: Boolean = true): TaskService[IterableApplicationData]\n\n  /**\n   * Returns the number of times the first letter of a app is repeated alphabetically\n   *\n   * @return the Seq[cards.nine.models.TermCounter]\n   * @throws PersistenceServiceException if exist some problem obtaining the apps\n   */\n  def fetchAlphabeticalAppsCounter: TaskService[Seq[TermCounter]]\n\n  /**\n   * Returns the number of times in every category alphabetically\n   *\n   * @return the Seq[cards.nine.models.TermCounter]\n   * @throws PersistenceServiceException if exist some problem obtaining the apps\n   */\n  def fetchCategorizedAppsCounter: TaskService[Seq[TermCounter]]\n\n  /**\n   * Returns the number of times by installation date\n   *\n   * @return the Seq[cards.nine.models.TermCounter]\n   * @throws PersistenceServiceException if exist some problem obtaining the apps\n   */\n  def fetchInstallationDateAppsCounter: TaskService[Seq[TermCounter]]\n\n  /**\n   * Obtains an app from the repository by the package name\n   *\n   * @param packageName the package name of the app to get\n   * @return an Option[cards.nine.models.App]\n   * @throws PersistenceServiceException if exist some problem obtaining the app\n   */\n  def findAppByPackage(packageName: String): TaskService[Option[Application]]\n\n  /**\n   * Obtains apps from the repository by the package names\n   *\n   * @param packageNames the package names of the apps to get\n   * @return an Seq[cards.nine.models.App]\n   * @throws PersistenceServiceException if exist some problem obtaining the app\n   */\n  def fetchAppByPackages(packageNames: Seq[String]): TaskService[Seq[Application]]\n\n  /**\n   * Adds an app to the repository\n   *\n   * @param app includes the necessary data to create a new app in the repository\n   * @return the cards.nine.models.App\n   * @throws PersistenceServiceException if exist some problem creating the app\n   */\n  def addApp(app: ApplicationData): TaskService[Application]\n\n  /**\n   * Adds a sequence of apps to the repository\n   *\n   * @param app includes the necessary data to create new apps in the repository\n   * @throws PersistenceServiceException if exist some problem creating apps\n   */\n  def addApps(app: Seq[ApplicationData]): TaskService[Unit]\n\n  /**\n   * Deletes all apps from the repository by the where clause\n   *\n   * @return an Int if the apps has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the apps\n   */\n  def deleteAllApps(): TaskService[Int]\n\n  /**\n   * Deletes some apps from the repository by the id\n   *\n   * @param ids the ids of the apps to delete\n   * @return an Int if the app has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the app\n   */\n  def deleteAppsByIds(ids: Seq[Int]): TaskService[Int]\n\n  /**\n   * Deletes an app from the repository by the package name\n   *\n   * @param packageName the package name of the app to delete\n   * @return an Int if the app has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the app\n   */\n  def deleteAppByPackage(packageName: String): TaskService[Int]\n\n  /**\n   * Updates the data of an app from the repository\n   *\n   * @param app includes the data to update the app\n   * @return an Int if the app has been updated correctly\n   * @throws PersistenceServiceException if exist some problem updating the app\n   */\n  def updateApp(app: Application): TaskService[Int]\n\n  /**\n   * Adds a card to the repository\n   *\n   * @param collectionId the collection where the card is included\n   * @param card includes the necessary data to create a new card in the repository\n   * @return the cards.nine.models.Card\n   * @throws PersistenceServiceException if exist some problem creating the card\n   */\n  def addCard(collectionId: Int, card: CardData): TaskService[Card]\n\n  /**\n   * Adds a sequence of cards to the repository\n   *\n   * @param cardsByCollectionId includes the collection where the cards are included and the cards\n   * @return the cards.nine.models.Card\n   * @throws PersistenceServiceException if exist some problem creating the card\n   */\n  def addCards(cardsByCollectionId: Seq[(Int, Seq[CardData])]): TaskService[Seq[Card]]\n\n  /**\n   * Deletes all cards from the repository by the where clause\n   *\n   * @return an Int if the cards has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the cards\n   */\n  def deleteAllCards(): TaskService[Int]\n\n  /**\n   * Deletes a card from the repository by the card\n   *\n   * @param collectionId the collection where the card is included\n   * @param cardId includes the card to delete\n   * @return an Int if the card has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the card\n   */\n  def deleteCard(collectionId: Int, cardId: Int): TaskService[Int]\n\n  /**\n   * Deletes a card from the repository by the card\n   *\n   * @param collectionId includes the collection where the card is included\n   * @param cardIds includes the cards to delete\n   * @return an Int if the card has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the card\n   */\n  def deleteCards(collectionId: Int, cardIds: Seq[Int]): TaskService[Int]\n\n  /**\n   * Deletes the cards from the repository by the collection id\n   *\n   * @param collectionId the id of the collection that contains the cards\n   * @return an Int if the cards have been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the cards\n   */\n  def deleteCardsByCollection(collectionId: Int): TaskService[Int]\n\n  /**\n   * Obtains all the cards from the repository by the collection id\n   *\n   * @param collectionId the id of the collection\n   * @return the Seq[cards.nine.models.Card]\n   * @throws PersistenceServiceException if exist some problem obtaining the cards\n   */\n  def fetchCardsByCollection(collectionId: Int): TaskService[Seq[Card]]\n\n  /**\n   * Obtains all the cards from the repository\n   *\n   * @return the Seq[cards.nine.models.Card]\n   * @throws PersistenceServiceException if exist some problem obtaining the cards\n   */\n  def fetchCards: TaskService[Seq[Card]]\n\n  /**\n   * Obtains a card from the repository by the id\n   *\n   * @param cardId the id of the card to find\n   * @return an Option[cards.nine.models.Card]\n   * @throws PersistenceServiceException if exist some problem obtaining the card\n   */\n  def findCardById(cardId: Int): TaskService[Option[Card]]\n\n  /**\n   * Updates the data of an card from the repository\n   *\n   * @param card includes the data to update the card\n   * @return an Int if the card has been updated correctly\n   * @throws PersistenceServiceException if exist some problem updating the card\n   */\n  def updateCard(card: Card): TaskService[Int]\n\n  /**\n   * Bulk update of the data of some cards from the repository\n   *\n   * @param cards includes the data to update the cards\n   * @return a Seq[Int] if the cards has been updated correctly\n   * @throws PersistenceServiceException if exist some problem updating the card\n   */\n  def updateCards(cards: Seq[Card]): TaskService[Seq[Int]]\n\n  /**\n   * Adds an collection to the repository\n   *\n   * @param collection includes the necessary data to create a new collection in the repository\n   * @return the [cards.nine.models.Collection]\n   * @throws PersistenceServiceException if exist some problem creating the collection\n   */\n  def addCollection(collection: CollectionData): TaskService[Collection]\n\n  /**\n   * Adds collections to the repository\n   *\n   * @param collections includes the necessary data to create new collections in the repository\n   * @return the Seq[cards.nine.models.Collection]\n   * @throws PersistenceServiceException if exist some problem creating the collection\n   */\n  def addCollections(collections: Seq[CollectionData]): TaskService[Seq[Collection]]\n\n  /**\n   * Deletes all collections from the repository by the where clause\n   *\n   * @return an Int if the collections has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the collections\n   */\n  def deleteAllCollections(): TaskService[Int]\n\n  /**\n   * Deletes a collection from the repository by the collection\n   *\n   * @param collection the collection to delete\n   * @return an Int if the collection has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the collection\n   */\n  def deleteCollection(collection: Collection): TaskService[Int]\n\n  /**\n   * Obtains all the collections from the repository\n   *\n   * @return the Seq[cards.nine.models.Collection]\n   * @throws PersistenceServiceException if exist some problem obtaining the collections\n   */\n  def fetchCollections: TaskService[Seq[Collection]]\n\n  /**\n   * Obtains the collection from the repository by the sharedCollection id\n   *\n   * @param sharedCollectionId the shared collection public identifier\n   * @return an Option[cards.nine.models.Collection]\n   * @throws PersistenceServiceException if exist some problem obtaining the collection\n   */\n  def fetchCollectionBySharedCollectionId(\n      sharedCollectionId: String): TaskService[Option[Collection]]\n\n  /**\n   * Obtains some collections based on a sequence of shared collection ids\n   *\n   * @param sharedCollectionIds the shared collection ids sequence\n   * @return a Seq[cards.nine.models.Collection] that could have less\n   *         size than the sharedCollectionIds.\n   *         IMPORTANT: The collections doesn't have populated the cards and moments fields\n   * @throws PersistenceServiceException if exist some problem obtaining the sequence of collections\n   */\n  def fetchCollectionsBySharedCollectionIds(\n      sharedCollectionIds: Seq[String]): TaskService[Seq[Collection]]\n\n  /**\n   * Obtains the collection from the repository by the position\n   *\n   * @param position the position\n   * @return an Option[cards.nine.models.Collection]\n   * @throws PersistenceServiceException if exist some problem obtaining the collection\n   */\n  def fetchCollectionByPosition(position: Int): TaskService[Option[Collection]]\n\n  /**\n   * Obtains a collection from the repository by the id\n   *\n   * @param collectionId the id of the collection to find\n   * @return an Option[cards.nine.models.Collection]\n   * @throws PersistenceServiceException if exist some problem obtaining the collection\n   */\n  def findCollectionById(collectionId: Int): TaskService[Option[Collection]]\n\n  /**\n   * Obtains a collection from the repository by category\n   *\n   * @param category category of collection\n   * @return an Option[cards.nine.models.Collection]\n   * @throws PersistenceServiceException if exist some problem obtaining the collection\n   */\n  def findCollectionByCategory(category: String): TaskService[Option[Collection]]\n\n  /**\n   * Updates the data of an collection from the repository\n   *\n   * @param collection includes the data to update the collection\n   * @return an Int if the collection has been updated correctly\n   * @throws PersistenceServiceException if exist some problem updating the collection\n   */\n  def updateCollection(collection: Collection): TaskService[Int]\n\n  /**\n   * Bulk update of the data of some collections from the repository\n   *\n   * @param collections includes the data to update the cards\n   * @return a Seq[Int] if the cards has been updated correctly\n   * @throws PersistenceServiceException if exist some problem updating the card\n   */\n  def updateCollections(collections: Seq[Collection]): TaskService[Seq[Int]]\n\n  /**\n   * Obtains the android id from the repository\n   *\n   * @return an String with the android id\n   * @throws AndroidIdNotFoundException if exist some problem obtaining the android id\n   */\n  def getAndroidId(implicit context: ContextSupport): TaskService[String]\n\n  /**\n   * Adds an user to the repository\n   *\n   * @param user includes the necessary data to create a new user in the repository\n   * @return the cards.nine.models.User\n   * @throws PersistenceServiceException if exist some problem creating the user\n   */\n  def addUser(user: UserData): TaskService[User]\n\n  /**\n   * Deletes all users from the repository by the where clause\n   *\n   * @return an Int if the users has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the users\n   */\n  def deleteAllUsers(): TaskService[Int]\n\n  /**\n   * Deletes an user from the repository by the user\n   *\n   * @param user includes the user to delete\n   * @return an Int if the user has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the user\n   */\n  def deleteUser(user: User): TaskService[Int]\n\n  /**\n   * Obtains all the users from the repository\n   *\n   * @return the Seq[cards.nine.models.User]\n   * @throws PersistenceServiceException if exist some problem obtaining the users\n   */\n  def fetchUsers: TaskService[Seq[User]]\n\n  /**\n   * Obtains an user from the repository by the id\n   *\n   * @param userId includes the user id  of the user to get\n   * @return an Option[cards.nine.models.User]\n   * @throws PersistenceServiceException if exist some problem obtaining the user\n   */\n  def findUserById(userId: Int): TaskService[Option[User]]\n\n  /**\n   * Updates the data of an user from the repository\n   *\n   * @param user includes the data to update the user\n   * @return an Int if the user has been updated correctly\n   * @throws PersistenceServiceException if exist some problem updating the user\n   */\n  def updateUser(user: User): TaskService[Int]\n\n  /**\n   * Creates or updates dock app to the repository\n   *\n   * @param dockApps includes the necessary data to create a sequence of new dock apps in the repository\n   * @return the Seq[cards.nine.models.DockApp]\n   * @throws PersistenceServiceException if exist some problem creating or updating the dock app\n   */\n  def createOrUpdateDockApp(dockApps: Seq[DockAppData]): TaskService[Seq[DockApp]]\n\n  /**\n   * Deletes all dock apps from the repository by the where clause\n   *\n   * @return an Int if the dock apps has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the dock apps\n   */\n  def deleteAllDockApps(): TaskService[Int]\n\n  /**\n   * Delete dock apps by position\n   *\n   * @param position position that you want to remove\n   * @throws PersistenceServiceException if exist some problem deleting the dock apps\n   */\n  def deleteDockAppByPosition(position: Int): TaskService[Unit]\n\n  /**\n   * Deletes a dock app from the repository by the dock app\n   *\n   * @param dockApp includes the dock app to delete\n   * @return an Int if the dock app has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the dock app\n   */\n  def deleteDockApp(dockApp: DockApp): TaskService[Int]\n\n  /**\n   * Obtains all the dock apps from the repository\n   *\n   * @return the Seq[cards.nine.models.DockApp]\n   * @throws PersistenceServiceException if exist some problem obtaining the dock apps\n   */\n  def fetchDockApps: TaskService[Seq[DockApp]]\n\n  /**\n   * Obtains a dock app from the repository by the id\n   *\n   * @param dockAppId includes the dock app id  of the dock app to get\n   * @return an Option[cards.nine.models.DockApp]\n   * @throws PersistenceServiceException if exist some problem obtaining the dock app\n   */\n  def findDockAppById(dockAppId: Int): TaskService[Option[DockApp]]\n\n  /**\n   * Adds an moment to the repository\n   *\n   * @param moment includes the necessary data to create a new moment in the repository\n   * @return the cards.nine.models.Moment\n   * @throws PersistenceServiceException if exist some problem creating the moment\n   */\n  def addMoment(moment: MomentData): TaskService[Moment]\n\n  /**\n   * Adds moments to the repository\n   *\n   * @param momentsWithWidgets includes the necessary data to create new moments in the repository and\n   *                           the necessary data to create the widgets associates with that moment\n   * @return the Seq[cards.nine.models.Moment]\n   * @throws PersistenceServiceException if exist some problem creating the moments\n   */\n  def addMoments(momentsWithWidgets: Seq[MomentData]): TaskService[Seq[Moment]]\n\n  /**\n   * Deletes all moments from the repository by the where clause\n   *\n   * @return an Int if the moments has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the moments\n   */\n  def deleteAllMoments(): TaskService[Int]\n\n  /**\n   * Delete an moment by id\n   *\n   * @param momentId includes the moment id to delete\n   * @return an Int if the moment has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the moment\n   */\n  def deleteMoment(momentId: Int): TaskService[Int]\n\n  /**\n   * Obtains all the moments from the repository\n   *\n   * @return the Seq[cards.nine.models.Moment]\n   * @throws PersistenceServiceException if exist some problem obtaining the moments\n   */\n  def fetchMoments: TaskService[Seq[Moment]]\n\n  /**\n   * Obtains an moment from the repository by the id\n   *\n   * @param momentId the moment id of the moment to get\n   * @return an Option[cards.nine.models.Moment]\n   * @throws PersistenceServiceException if exist some problem obtaining the moment\n   */\n  def findMomentById(momentId: Int): TaskService[Option[Moment]]\n\n  /**\n   * Obtains an moment from the repository by the collectionId\n   *\n   * @param collectionId the collection id of the moment to get\n   * @return an Option[cards.nine.models.Moment]\n   * @throws PersistenceServiceException if exist some problem obtaining the moment\n   */\n  def getMomentByCollectionId(collectionId: Int): TaskService[Option[Moment]]\n\n  /**\n   * Obtains an moment from the repository by type. Return exception if the type doesn't exist\n   *\n   * @param momentType type of the moment\n   * @return an cards.nine.models.Moment\n   * @throws PersistenceServiceException if exist some problem obtaining the moment\n   */\n  def getMomentByType(momentType: NineCardsMoment): TaskService[Moment]\n\n  /**\n   * Obtains an moment from the repository by type. Return None if the type doesn't exist\n   *\n   * @param momentType type of the moment\n   * @return an cards.nine.models.Moment\n   * @throws PersistenceServiceException if exist some problem obtaining the moment\n   */\n  def fetchMomentByType(momentType: String): TaskService[Option[Moment]]\n\n  /**\n   * Obtains an moment from the repository by id. Return None if the type doesn't exist\n   *\n   * @param momentId id of the moment\n   * @return an cards.nine.models.Moment\n   * @throws PersistenceServiceException if exist some problem obtaining the moment\n   */\n  def fetchMomentById(momentId: Int): TaskService[Option[Moment]]\n\n  /**\n   * Updates the data of an moment from the repository\n   *\n   * @param moment includes the data to update the moment\n   * @return an Int if the moment has been updated correctly\n   * @throws PersistenceServiceException if exist some problem updating the moment\n   */\n  def updateMoment(moment: Moment): TaskService[Int]\n\n  /**\n   * Add a widget to the repository\n   *\n   * @param widget includes the necessary data to create a new widget in the repository\n   * @return the cards.nine.models.Widget\n   * @throws PersistenceServiceException if exist some problem creating the widgets\n   */\n  def addWidget(widget: WidgetData): TaskService[Widget]\n\n  /**\n   * Adds widgets to the repository\n   *\n   * @param widgets includes the necessary data to create new widgets in the repository\n   * @return the Seq[cards.nine.models.Widget]\n   * @throws PersistenceServiceException if exist some problem creating the widgets\n   */\n  def addWidgets(widgets: Seq[WidgetData]): TaskService[Seq[Widget]]\n\n  /**\n   * Deletes all widgets from the repository\n   *\n   * @return an Int if the widgets has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the widgets\n   */\n  def deleteAllWidgets(): TaskService[Int]\n\n  /**\n   * Deletes a widget from the repository\n   *\n   * @param widget includes the widget to delete\n   * @return an Int if the widget has been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the widget\n   */\n  def deleteWidget(widget: Widget): TaskService[Int]\n\n  /**\n   * Deletes the widgets from the repository by the moment id\n   *\n   * @param momentId the id of the moment that contains the widgets\n   * @return an Int if the widgets have been deleted correctly\n   * @throws PersistenceServiceException if exist some problem deleting the widgets\n   */\n  def deleteWidgetsByMoment(momentId: Int): TaskService[Int]\n\n  /**\n   * Obtains all the widgets from the repository\n   *\n   * @return the Seq[cards.nine.models.Widget]\n   * @throws PersistenceServiceException if exist some problem obtaining the widgets\n   */\n  def fetchWidgets: TaskService[Seq[Widget]]\n\n  /**\n   * Obtains a widget from the repository by the id\n   *\n   * @param widgetId the widget id  of the widget to get\n   * @return an Option[cards.nine.models.Widget]\n   * @throws PersistenceServiceException if exist some problem obtaining the widget\n   */\n  def findWidgetById(widgetId: Int): TaskService[Option[Widget]]\n\n  /**\n   * Obtains the widget from the repository by the appWidgetId\n   *\n   * @param appWidgetId the appWidgetId value\n   * @return an Option[cards.nine.models.Widget]\n   * @throws PersistenceServiceException if exist some problem obtaining the widget\n   */\n  def fetchWidgetByAppWidgetId(appWidgetId: Int): TaskService[Option[Widget]]\n\n  /**\n   * Obtains all widgets from the repository by the moment id\n   *\n   * @param momentId id of the moment\n   * @return the Seq[cards.nine.models.Widget]\n   * @throws PersistenceServiceException if exist some problem obtaining the widgets\n   */\n  def fetchWidgetsByMoment(momentId: Int): TaskService[Seq[Widget]]\n\n  /**\n   * Updates the data of a widget from the repository\n   *\n   * @param widget includes the data to update the widget\n   * @return an Int if the widget has been updated correctly\n   * @throws PersistenceServiceException if exist some problem updating the widget\n   */\n  def updateWidget(widget: Widget): TaskService[Int]\n\n  /**\n   * Bulk update of the data of some widgets from the repository\n   *\n   * @param widgets includes the data to update the widgets\n   * @return a Seq[Int] if the widgets has been updated correctly\n   * @throws PersistenceServiceException if exist some problem updating the widget\n   */\n  def updateWidgets(widgets: Seq[Widget]): TaskService[Seq[Int]]\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/conversions/AppConversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.conversions\n\nimport cards.nine.models.types.NineCardsCategory\nimport cards.nine.models.{Application, ApplicationData}\nimport cards.nine.repository.model.{App => RepositoryApp, AppData => RepositoryAppData}\n\ntrait AppConversions {\n\n  def toApp(app: RepositoryApp): Application =\n    Application(\n      id = app.id,\n      name = app.data.name,\n      packageName = app.data.packageName,\n      className = app.data.className,\n      category = NineCardsCategory(app.data.category),\n      dateInstalled = app.data.dateInstalled,\n      dateUpdated = app.data.dateUpdate,\n      version = app.data.version,\n      installedFromGooglePlay = app.data.installedFromGooglePlay)\n\n  def toRepositoryApp(app: Application): RepositoryApp =\n    RepositoryApp(\n      id = app.id,\n      data = RepositoryAppData(\n        name = app.name,\n        packageName = app.packageName,\n        className = app.className,\n        category = app.category.name,\n        dateInstalled = app.dateInstalled,\n        dateUpdate = app.dateUpdated,\n        version = app.version,\n        installedFromGooglePlay = app.installedFromGooglePlay))\n\n  def toRepositoryAppData(app: ApplicationData): RepositoryAppData =\n    RepositoryAppData(\n      name = app.name,\n      packageName = app.packageName,\n      className = app.className,\n      category = app.category.name,\n      dateInstalled = app.dateInstalled,\n      dateUpdate = app.dateUpdated,\n      version = app.version,\n      installedFromGooglePlay = app.installedFromGooglePlay)\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/conversions/CardConversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.conversions\n\nimport cards.nine.models.types.CardType\nimport cards.nine.models.{Card, CardData}\nimport cards.nine.repository.model.{\n  Card => RepositoryCard,\n  CardData => RepositoryCardData,\n  CardsWithCollectionId\n}\nimport cards.nine.models.NineCardsIntentConversions\n\ntrait CardConversions extends NineCardsIntentConversions {\n\n  def toCardsWithCollectionId(cardsByCollectionId: (Int, Seq[CardData])): CardsWithCollectionId = {\n    val (collectionId, cards) = cardsByCollectionId\n    CardsWithCollectionId(collectionId = collectionId, data = cards map toRepositoryCardData)\n  }\n\n  def toCard(card: RepositoryCard): Card = {\n    Card(\n      id = card.id,\n      position = card.data.position,\n      term = card.data.term,\n      packageName = card.data.packageName,\n      cardType = CardType(card.data.cardType),\n      intent = jsonToNineCardIntent(card.data.intent),\n      imagePath = card.data.imagePath,\n      notification = card.data.notification)\n  }\n\n  def toRepositoryCard(card: Card): RepositoryCard =\n    RepositoryCard(\n      id = card.id,\n      data = RepositoryCardData(\n        position = card.position,\n        term = card.term,\n        packageName = card.packageName,\n        cardType = card.cardType.name,\n        intent = nineCardIntentToJson(card.intent),\n        imagePath = card.imagePath,\n        notification = card.notification\n      )\n    )\n\n  def toRepositoryCardData(card: CardData): RepositoryCardData =\n    RepositoryCardData(\n      position = card.position,\n      term = card.term,\n      packageName = card.packageName,\n      cardType = card.cardType.name,\n      intent = nineCardIntentToJson(card.intent),\n      imagePath = card.imagePath,\n      notification = card.notification)\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/conversions/CollectionConversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.conversions\n\nimport cards.nine.models.types._\nimport cards.nine.models.{Collection, CollectionData}\nimport cards.nine.repository.model.{\n  Card => RepositoryCard,\n  Collection => RepositoryCollection,\n  CollectionData => RepositoryCollectionData\n}\n\ntrait CollectionConversions extends CardConversions with MomentConversions {\n\n  def toCollection(collection: RepositoryCollection): Collection =\n    Collection(\n      id = collection.id,\n      position = collection.data.position,\n      name = collection.data.name,\n      collectionType = CollectionType(collection.data.collectionType),\n      icon = collection.data.icon,\n      themedColorIndex = collection.data.themedColorIndex,\n      appsCategory = collection.data.appsCategory.map(NineCardsCategory(_)),\n      originalSharedCollectionId = collection.data.originalSharedCollectionId,\n      sharedCollectionId = collection.data.sharedCollectionId,\n      sharedCollectionSubscribed = collection.data.sharedCollectionSubscribed getOrElse false,\n      moment = None,\n      publicCollectionStatus = determinePublicCollectionStatus(collection))\n\n  def toCollection(collection: RepositoryCollection, cards: Seq[RepositoryCard]): Collection =\n    Collection(\n      id = collection.id,\n      position = collection.data.position,\n      name = collection.data.name,\n      collectionType = CollectionType(collection.data.collectionType),\n      icon = collection.data.icon,\n      themedColorIndex = collection.data.themedColorIndex,\n      appsCategory = collection.data.appsCategory.map(NineCardsCategory(_)),\n      originalSharedCollectionId = collection.data.originalSharedCollectionId,\n      sharedCollectionId = collection.data.sharedCollectionId,\n      sharedCollectionSubscribed = collection.data.sharedCollectionSubscribed getOrElse false,\n      cards = cards map toCard,\n      moment = None,\n      publicCollectionStatus = determinePublicCollectionStatus(collection))\n\n  def toRepositoryCollection(collection: Collection): RepositoryCollection =\n    RepositoryCollection(\n      id = collection.id,\n      data = RepositoryCollectionData(\n        position = collection.position,\n        name = collection.name,\n        collectionType = collection.collectionType.name,\n        icon = collection.icon,\n        themedColorIndex = collection.themedColorIndex,\n        appsCategory = collection.appsCategory map (_.name),\n        originalSharedCollectionId = collection.originalSharedCollectionId,\n        sharedCollectionId = collection.sharedCollectionId,\n        sharedCollectionSubscribed = Option(collection.sharedCollectionSubscribed)\n      )\n    )\n\n  def toRepositoryCollectionData(collection: CollectionData): RepositoryCollectionData =\n    RepositoryCollectionData(\n      position = collection.position,\n      name = collection.name,\n      collectionType = collection.collectionType.name,\n      icon = collection.icon,\n      themedColorIndex = collection.themedColorIndex,\n      appsCategory = collection.appsCategory map (_.name),\n      originalSharedCollectionId = collection.originalSharedCollectionId,\n      sharedCollectionId = collection.sharedCollectionId,\n      sharedCollectionSubscribed = Option(collection.sharedCollectionSubscribed))\n\n  private[this] def determinePublicCollectionStatus(\n      repositoryCollection: RepositoryCollection): PublicCollectionStatus =\n    repositoryCollection match {\n      case collection\n          if collection.data.sharedCollectionId.isDefined && collection.data.originalSharedCollectionId == collection.data.sharedCollectionId =>\n        PublishedByOther\n      case collection if collection.data.sharedCollectionId.isDefined =>\n        PublishedByMe\n      case _ =>\n        NotPublished\n    }\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/conversions/Conversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.conversions\n\nimport cards.nine.models.TermCounter\nimport cards.nine.repository.model.{DataCounter => RepoDataCounter}\n\ntrait Conversions\n    extends AppConversions\n    with CardConversions\n    with CollectionConversions\n    with UserConversions\n    with MomentConversions\n    with DockAppConversions\n    with WidgetConversions {\n\n  def toDataCounterSeq(items: Seq[RepoDataCounter]): Seq[TermCounter] = items map toDataCounter\n\n  def toDataCounter(item: RepoDataCounter): TermCounter =\n    TermCounter(term = item.term, count = item.count)\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/conversions/DockAppConversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.conversions\n\nimport cards.nine.models.types.DockType\nimport cards.nine.models.{DockApp, DockAppData, NineCardsIntentConversions}\nimport cards.nine.repository.model.{\n  DockApp => RepositoryDockApp,\n  DockAppData => RepositoryDockAppData\n}\n\ntrait DockAppConversions extends NineCardsIntentConversions {\n\n  def toDockApp(dockApp: RepositoryDockApp): DockApp =\n    DockApp(\n      id = dockApp.id,\n      name = dockApp.data.name,\n      dockType = DockType(dockApp.data.dockType),\n      intent = jsonToNineCardIntent(dockApp.data.intent),\n      imagePath = dockApp.data.imagePath,\n      position = dockApp.data.position)\n\n  def toRepositoryDockApp(dockApp: DockApp): RepositoryDockApp =\n    RepositoryDockApp(\n      id = dockApp.id,\n      data = RepositoryDockAppData(\n        name = dockApp.name,\n        dockType = dockApp.dockType.name,\n        intent = nineCardIntentToJson(dockApp.intent),\n        imagePath = dockApp.imagePath,\n        position = dockApp.position))\n\n  def toRepositoryDockApp(id: Int, dockApp: DockAppData): RepositoryDockApp =\n    RepositoryDockApp(id = id, data = toRepositoryDockAppData(dockApp))\n\n  def toRepositoryDockAppData(dockApp: DockAppData): RepositoryDockAppData =\n    RepositoryDockAppData(\n      name = dockApp.name,\n      dockType = dockApp.dockType.name,\n      intent = nineCardIntentToJson(dockApp.intent),\n      imagePath = dockApp.imagePath,\n      position = dockApp.position)\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/conversions/MomentConversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.conversions\n\nimport cards.nine.models.reads.MomentImplicits\nimport cards.nine.models.types.NineCardsMoment\nimport cards.nine.models.{Moment, MomentData, MomentTimeSlot}\nimport cards.nine.repository.model.{Moment => RepositoryMoment, MomentData => RepositoryMomentData}\nimport play.api.libs.json.Json\n\ntrait MomentConversions {\n\n  import MomentImplicits._\n\n  def toMoment(moment: RepositoryMoment): Moment =\n    Moment(\n      id = moment.id,\n      collectionId = moment.data.collectionId,\n      timeslot = Json.parse(moment.data.timeslot).as[Seq[MomentTimeSlot]],\n      wifi = if (moment.data.wifi.isEmpty) List.empty else moment.data.wifi.split(\",\").toList,\n      bluetooth =\n        if (moment.data.bluetooth.isEmpty) List.empty else moment.data.bluetooth.split(\",\").toList,\n      headphone = moment.data.headphone,\n      momentType = NineCardsMoment(moment.data.momentType),\n      widgets = None)\n\n  def toRepositoryMoment(moment: Moment): RepositoryMoment =\n    RepositoryMoment(id = moment.id, data = toRepositoryMomentData(moment.toData))\n\n  def toRepositoryMomentWithoutCollection(moment: Moment): RepositoryMoment =\n    RepositoryMoment(\n      id = moment.id,\n      data = toRepositoryMomentData(moment.toData).copy(collectionId = None))\n\n  def toRepositoryMomentData(moment: MomentData): RepositoryMomentData =\n    RepositoryMomentData(\n      collectionId = moment.collectionId,\n      timeslot = Json.toJson(moment.timeslot).toString,\n      wifi = moment.wifi.mkString(\",\"),\n      bluetooth = moment.bluetooth.mkString(\",\"),\n      headphone = moment.headphone,\n      momentType = Option(moment.momentType.name))\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/conversions/UserConversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.conversions\n\nimport cards.nine.models.{User, UserData, UserProfile}\nimport cards.nine.repository.model.{User => RepositoryUser, UserData => RepositoryUserData}\nimport cards.nine.services.persistence._\n\ntrait UserConversions {\n\n  def toUser(user: RepositoryUser): User =\n    User(\n      id = user.id,\n      email = user.data.email,\n      apiKey = user.data.apiKey,\n      sessionToken = user.data.sessionToken,\n      deviceToken = user.data.deviceToken,\n      marketToken = user.data.marketToken,\n      deviceName = user.data.deviceName,\n      deviceCloudId = user.data.deviceCloudId,\n      userProfile =\n        UserProfile(name = user.data.name, avatar = user.data.avatar, cover = user.data.cover))\n\n  def toRepositoryUser(user: User): RepositoryUser =\n    RepositoryUser(\n      id = user.id,\n      data = RepositoryUserData(\n        email = user.email,\n        apiKey = user.apiKey,\n        sessionToken = user.sessionToken,\n        deviceToken = user.deviceToken,\n        marketToken = user.marketToken,\n        name = user.userProfile.name,\n        avatar = user.userProfile.avatar,\n        cover = user.userProfile.cover,\n        deviceName = user.deviceName,\n        deviceCloudId = user.deviceCloudId))\n\n  def toRepositoryUserData(user: UserData): RepositoryUserData =\n    RepositoryUserData(\n      email = user.email,\n      apiKey = user.apiKey,\n      sessionToken = user.sessionToken,\n      deviceToken = user.deviceToken,\n      marketToken = user.marketToken,\n      name = user.userProfile.name,\n      avatar = user.userProfile.avatar,\n      cover = user.userProfile.cover,\n      deviceName = user.deviceName,\n      deviceCloudId = user.deviceCloudId)\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/conversions/WidgetConversions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.conversions\n\nimport cards.nine.models.types.WidgetType\nimport cards.nine.models.{NineCardsIntentConversions, Widget, WidgetArea, WidgetData}\nimport cards.nine.repository.model.{Widget => RepositoryWidget, WidgetData => RepositoryWidgetData}\n\ntrait WidgetConversions extends NineCardsIntentConversions {\n\n  def toWidget(widget: RepositoryWidget): Widget =\n    Widget(\n      id = widget.id,\n      momentId = widget.data.momentId,\n      packageName = widget.data.packageName,\n      className = widget.data.className,\n      appWidgetId = if (widget.data.appWidgetId == 0) None else Some(widget.data.appWidgetId),\n      area = WidgetArea(\n        startX = widget.data.startX,\n        startY = widget.data.startY,\n        spanX = widget.data.spanX,\n        spanY = widget.data.spanY),\n      widgetType = WidgetType(widget.data.widgetType),\n      label = widget.data.label,\n      imagePath = widget.data.imagePath,\n      intent = widget.data.intent map jsonToNineCardIntent)\n\n  def toRepositoryWidget(widget: Widget): RepositoryWidget =\n    RepositoryWidget(\n      id = widget.id,\n      data = RepositoryWidgetData(\n        momentId = widget.momentId,\n        packageName = widget.packageName,\n        className = widget.className,\n        appWidgetId = widget.appWidgetId getOrElse 0,\n        startX = widget.area.startX,\n        startY = widget.area.startY,\n        spanX = widget.area.spanX,\n        spanY = widget.area.spanY,\n        widgetType = widget.widgetType.name,\n        label = widget.label,\n        imagePath = widget.imagePath,\n        intent = widget.intent map nineCardIntentToJson))\n\n  def toRepositoryWidgetData(widget: WidgetData): RepositoryWidgetData =\n    RepositoryWidgetData(\n      momentId = widget.momentId,\n      packageName = widget.packageName,\n      className = widget.className,\n      appWidgetId = widget.appWidgetId getOrElse 0,\n      startX = widget.area.startX,\n      startY = widget.area.startY,\n      spanX = widget.area.spanX,\n      spanY = widget.area.spanY,\n      widgetType = widget.widgetType.name,\n      label = widget.label,\n      imagePath = widget.imagePath,\n      intent = widget.intent map nineCardIntentToJson)\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/impl/AndroidPersistenceServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport android.database.Cursor\nimport android.net.Uri\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.javaNull\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.services.persistence.{AndroidIdNotFoundException, PersistenceServices}\nimport monix.eval.Task\n\ntrait AndroidPersistenceServicesImpl extends PersistenceServices {\n\n  val androidId = \"android_id\"\n\n  val contentGServices = \"content://com.google.android.gsf.gservices\"\n\n  def getAndroidId(implicit context: ContextSupport): TaskService[String] =\n    TaskService {\n      Task {\n        val cursor: Option[Cursor] = Option(\n          context.getContentResolver\n            .query(Uri.parse(contentGServices), javaNull, javaNull, Array(androidId), javaNull))\n        val result: Option[String] = cursor filter (c =>\n                                                      c.moveToFirst && c.getColumnCount >= 2) map (_.getLong(\n            1).toHexString.toUpperCase)\n        cursor foreach (_.close())\n        result match {\n          case Some(r) => Right(r)\n          case _       => Left(AndroidIdNotFoundException(\"Android Id not found\"))\n        }\n      }\n    }\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/impl/AppPersistenceServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.{FetchAppOrder, OrderByCategory, OrderByInstallDate, OrderByName}\nimport cards.nine.models.{Application, ApplicationData, IterableAppCursor, IterableApplicationData}\nimport cards.nine.repository.model.{App => RepositoryApp}\nimport cards.nine.repository.provider.{AppEntity, NineCardsSqlHelper}\nimport cards.nine.services.persistence._\nimport cards.nine.services.persistence.conversions.Conversions\n\ntrait AppPersistenceServicesImpl extends PersistenceServices {\n\n  self: Conversions with PersistenceDependencies with ImplicitsPersistenceServiceExceptions =>\n\n  def fetchApps(orderBy: FetchAppOrder, ascending: Boolean = true) = {\n    val orderByString = toOrderBy(orderBy, ascending)\n\n    val appSeq = for {\n      apps <- appRepository.fetchApps(orderByString)\n    } yield apps map toApp\n\n    appSeq.resolve[PersistenceServiceException]\n  }\n\n  def findAppByPackage(packageName: String) =\n    (for {\n      app <- appRepository.fetchAppByPackage(packageName)\n    } yield app map toApp).resolve[PersistenceServiceException]\n\n  def fetchAppByPackages(packageNames: Seq[String]) =\n    (for {\n      app <- appRepository.fetchAppByPackages(packageNames)\n    } yield app map toApp).resolve[PersistenceServiceException]\n\n  def addApp(app: ApplicationData) =\n    (for {\n      app <- appRepository.addApp(toRepositoryAppData(app))\n    } yield toApp(app)).resolve[PersistenceServiceException]\n\n  def addApps(app: Seq[ApplicationData]) =\n    (for {\n      _ <- appRepository.addApps(app map toRepositoryAppData)\n    } yield ()).resolve[PersistenceServiceException]\n\n  def deleteAllApps() =\n    (for {\n      deleted <- appRepository.deleteApps()\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def deleteAppsByIds(ids: Seq[Int]) =\n    (for {\n      deleted <- appRepository.deleteApps(s\"${NineCardsSqlHelper.id} IN (${ids.mkString(\",\")})\")\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def deleteAppByPackage(packageName: String) =\n    (for {\n      deleted <- appRepository.deleteAppByPackage(packageName)\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def updateApp(app: Application) =\n    (for {\n      updated <- appRepository.updateApp(toRepositoryApp(app))\n    } yield updated).resolve[PersistenceServiceException]\n\n  def fetchIterableApps(orderBy: FetchAppOrder, ascending: Boolean = true) = {\n    val orderByString = toOrderBy(orderBy, ascending)\n\n    val appSeq: TaskService[IterableApplicationData] = for {\n      iter <- appRepository.fetchIterableApps(orderBy = orderByString)\n    } yield new IterableAppCursor[RepositoryApp](iter, toApp)\n\n    appSeq.resolve[PersistenceServiceException]\n  }\n\n  def fetchIterableAppsByKeyword(\n      keyword: String,\n      orderBy: FetchAppOrder,\n      ascending: Boolean = true) = {\n    val orderByString = toOrderBy(orderBy, ascending)\n\n    val appSeq: TaskService[IterableApplicationData] = for {\n      iter <- appRepository.fetchIterableApps(\n        where = toStringWhere,\n        whereParams = Seq(s\"%$keyword%\"),\n        orderBy = orderByString)\n    } yield new IterableAppCursor[RepositoryApp](iter, toApp)\n\n    appSeq.resolve[PersistenceServiceException]\n  }\n\n  def fetchAppsByCategory(category: String, orderBy: FetchAppOrder, ascending: Boolean = true) = {\n    val orderByString = toOrderBy(orderBy, ascending)\n\n    val appSeq = for {\n      apps <- appRepository.fetchAppsByCategory(category = category, orderBy = orderByString)\n    } yield apps map toApp\n\n    appSeq.resolve[PersistenceServiceException]\n  }\n\n  def fetchIterableAppsByCategory(\n      category: String,\n      orderBy: FetchAppOrder,\n      ascending: Boolean = true) = {\n    val orderByString = toOrderBy(orderBy, ascending)\n\n    val appSeq: TaskService[IterableApplicationData] = for {\n      iter <- appRepository\n        .fetchIterableAppsByCategory(category = category, orderBy = orderByString)\n    } yield new IterableAppCursor[RepositoryApp](iter, toApp)\n\n    appSeq.resolve[PersistenceServiceException]\n  }\n\n  def fetchAlphabeticalAppsCounter =\n    (for {\n      counters <- appRepository.fetchAlphabeticalAppsCounter\n    } yield toDataCounterSeq(counters)).resolve[PersistenceServiceException]\n\n  def fetchCategorizedAppsCounter =\n    (for {\n      counters <- appRepository.fetchCategorizedAppsCounter\n    } yield toDataCounterSeq(counters)).resolve[PersistenceServiceException]\n\n  def fetchInstallationDateAppsCounter =\n    (for {\n      counters <- appRepository.fetchInstallationDateAppsCounter\n    } yield toDataCounterSeq(counters)).resolve[PersistenceServiceException]\n\n  private[this] def toOrderBy(orderBy: FetchAppOrder, ascending: Boolean): String = {\n    val sort = if (ascending) \"ASC\" else \"DESC\"\n    orderBy match {\n      case OrderByName        => s\"${AppEntity.name} COLLATE NOCASE $sort\"\n      case OrderByInstallDate => s\"${AppEntity.dateInstalled} $sort\"\n      case OrderByCategory    => s\"${AppEntity.category} $sort, ${AppEntity.name} $sort\"\n    }\n  }\n\n  private[this] def toStringWhere: String = s\"${AppEntity.name} LIKE ? \"\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/impl/CardPersistenceServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{Card, CardData}\nimport cards.nine.repository.provider.{CardEntity, NineCardsSqlHelper}\nimport cards.nine.services.persistence._\nimport cards.nine.services.persistence.conversions.Conversions\n\ntrait CardPersistenceServicesImpl extends PersistenceServices {\n\n  self: Conversions with PersistenceDependencies with ImplicitsPersistenceServiceExceptions =>\n\n  def addCard(collectionId: Int, card: CardData) =\n    (for {\n      card <- cardRepository.addCard(collectionId, toRepositoryCardData(card))\n    } yield toCard(card)).resolve[PersistenceServiceException]\n\n  def addCards(cardsByCollectionId: Seq[(Int, Seq[CardData])]) =\n    (for {\n      cards <- cardRepository.addCards(cardsByCollectionId map toCardsWithCollectionId)\n    } yield cards map toCard).resolve[PersistenceServiceException]\n\n  def deleteAllCards() =\n    (for {\n      deleted <- cardRepository.deleteCards()\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def deleteCard(collectionId: Int, cardId: Int) =\n    (for {\n      deleted <- cardRepository.deleteCard(collectionId, cardId)\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def deleteCards(collectionId: Int, cardIds: Seq[Int]) = {\n    val where = s\"${NineCardsSqlHelper.id} IN (${cardIds.mkString(\",\")})\"\n    (for {\n      deleted <- cardRepository.deleteCards(Some(collectionId), where)\n    } yield deleted).resolve[PersistenceServiceException]\n  }\n\n  def deleteCardsByCollection(collectionId: Int) =\n    (for {\n      deleted <- cardRepository.deleteCards(\n        maybeCollectionId = None,\n        where = s\"${CardEntity.collectionId} = $collectionId\")\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def fetchCardsByCollection(collectionId: Int) =\n    (for {\n      cards <- cardRepository.fetchCardsByCollection(collectionId)\n    } yield cards map toCard).resolve[PersistenceServiceException]\n\n  def fetchCards =\n    (for {\n      cards <- cardRepository.fetchCards\n    } yield cards map toCard).resolve[PersistenceServiceException]\n\n  def findCardById(cardId: Int) =\n    (for {\n      maybeCard <- cardRepository.findCardById(cardId)\n    } yield maybeCard map toCard).resolve[PersistenceServiceException]\n\n  def updateCard(card: Card) =\n    (for {\n      updated <- cardRepository.updateCard(toRepositoryCard(card))\n    } yield updated).resolve[PersistenceServiceException]\n\n  def updateCards(cards: Seq[Card]) =\n    (for {\n      updated <- cardRepository.updateCards(cards map toRepositoryCard)\n    } yield updated).resolve[PersistenceServiceException]\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/impl/CollectionPersistenceServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{Collection, CollectionData, Moment, MomentData}\nimport cards.nine.repository.model.{Card => RepositoryCard, Collection => RepositoryCollection}\nimport cards.nine.repository.provider.{CardEntity, MomentEntity}\nimport cards.nine.services.persistence._\nimport cards.nine.services.persistence.conversions.Conversions\nimport cats.data.EitherT\nimport monix.eval.Task\n\ntrait CollectionPersistenceServicesImpl extends PersistenceServices {\n\n  self: Conversions\n    with PersistenceDependencies\n    with ImplicitsPersistenceServiceExceptions\n    with MomentPersistenceServicesImpl\n    with CardPersistenceServicesImpl =>\n\n  def addCollection(collection: CollectionData) =\n    (for {\n      collectionAdded <- collectionRepository.addCollection(toRepositoryCollectionData(collection))\n      addedCards      <- addCards(Seq((collectionAdded.id, collection.cards)))\n      _ <- createOrUpdateMoments(\n        Seq(collection.moment map (_.copy(collectionId = Option(collectionAdded.id)))).flatten)\n    } yield toCollection(collectionAdded).copy(cards = addedCards))\n      .resolve[PersistenceServiceException]\n\n  def addCollections(collections: Seq[CollectionData]) = {\n    val collectionsData = collections map toRepositoryCollectionData\n    val cardsData       = collections map (_.cards)\n    val momentsData     = collections map (_.moment)\n    (for {\n      collections <- collectionRepository.addCollections(collectionsData)\n      cards = collections.zip(cardsData) map {\n        case (collection, cardsRequest) => (collection.id, cardsRequest)\n      }\n      addedCards <- addCards(cards)\n      moments = collections.zip(momentsData) flatMap {\n        case (collection, momentRequest) =>\n          momentRequest map (_.copy(collectionId = Option(collection.id)))\n      }\n      _ <- createOrUpdateMoments(moments)\n    } yield collections map toCollection).resolve[PersistenceServiceException]\n  }\n\n  def deleteAllCollections() =\n    (for {\n      deleted <- collectionRepository.deleteCollections()\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def deleteCollection(collection: Collection) = {\n\n    def unlinkCollectionInMoment(maybeMoment: Option[Moment]): TaskService[Unit] = {\n      maybeMoment match {\n        case Some(moment) =>\n          momentRepository.updateMoment(toRepositoryMomentWithoutCollection(moment)) map (_ => ())\n        case None => TaskService(Task(Right((): Unit)))\n      }\n    }\n\n    (for {\n      _ <- cardRepository.deleteCards(where = s\"${CardEntity.collectionId} = ${collection.id}\")\n      deletedCollection <- collectionRepository.deleteCollection(\n        toRepositoryCollection(collection))\n      _ <- unlinkCollectionInMoment(collection.moment)\n    } yield deletedCollection).resolve[PersistenceServiceException]\n  }\n\n  def fetchCollections: EitherT[Task, NineCardException, Seq[Collection]] = {\n\n    def populateCollections(collections: Seq[RepositoryCollection]): TaskService[Seq[Collection]] = {\n      val tasks = collections map (collection =>\n                                     populateCollection(TaskService.right(Option(collection))).value)\n      TaskService(Task.gatherUnordered(tasks) map { list =>\n        Right(list.collect {\n          case Right(Some(collection)) => collection\n        })\n      })\n    }\n\n    (for {\n      collectionsWithoutCards <- collectionRepository.fetchSortedCollections\n      collectionWithCards     <- populateCollections(collectionsWithoutCards)\n    } yield collectionWithCards.sortWith(_.position < _.position))\n      .resolve[PersistenceServiceException]\n  }\n\n  def fetchCollectionBySharedCollectionId(sharedCollectionId: String) =\n    populateCollection(\n      collectionRepository.fetchCollectionBySharedCollectionId(sharedCollectionId))\n\n  def fetchCollectionsBySharedCollectionIds(sharedCollectionIds: Seq[String]) =\n    (for {\n      collections <- collectionRepository.fetchCollectionsBySharedCollectionIds(\n        sharedCollectionIds)\n    } yield collections map (toCollection(_, cards = Seq.empty)))\n      .resolve[PersistenceServiceException]\n\n  def fetchCollectionByPosition(position: Int) =\n    populateCollection(collectionRepository.fetchCollectionByPosition(position))\n\n  def findCollectionById(collectionId: Int) =\n    populateCollection(collectionRepository.findCollectionById(collectionId))\n\n  def findCollectionByCategory(category: String) =\n    populateCollection(collectionRepository.fetchCollectionsByCategory(category).map(_.headOption))\n\n  def updateCollection(collection: Collection) =\n    (for {\n      updated <- collectionRepository.updateCollection(toRepositoryCollection(collection))\n    } yield updated).resolve[PersistenceServiceException]\n\n  def updateCollections(collections: Seq[Collection]) =\n    (for {\n      updated <- collectionRepository.updateCollections(collections map toRepositoryCollection)\n    } yield updated).resolve[PersistenceServiceException]\n\n  private[this] def populateCollection(\n      service: TaskService[Option[RepositoryCollection]]): TaskService[Option[Collection]] = {\n\n    def getMomentsByCollection(\n        maybeCollection: Option[RepositoryCollection]): TaskService[Seq[Moment]] = {\n      maybeCollection match {\n        case Some(collection) =>\n          for {\n            moments <- momentRepository.fetchMoments(\n              where = s\"${MomentEntity.collectionId} = ${collection.id}\")\n          } yield moments map toMoment\n\n        case None => TaskService(Task(Right(Seq.empty)))\n      }\n    }\n\n    def fetchCards(\n        maybeCollection: Option[RepositoryCollection]): TaskService[Seq[RepositoryCard]] = {\n      maybeCollection match {\n        case Some(collection) => cardRepository.fetchCardsByCollection(collection.id)\n        case None             => TaskService(Task(Right(Seq.empty)))\n      }\n    }\n\n    (for {\n      maybeCollection <- service\n      cards           <- fetchCards(maybeCollection)\n      moments         <- getMomentsByCollection(maybeCollection)\n    } yield maybeCollection map (toCollection(_, cards).copy(moment = moments.headOption)))\n      .resolve[PersistenceServiceException]\n  }\n\n  private[this] def createOrUpdateMoments(moments: Seq[MomentData]): TaskService[Unit] = {\n\n    def createOrUpdateMoment(momentData: MomentData) = {\n\n      def createOrUpdate(maybeMoment: Option[Moment]) = maybeMoment match {\n        case Some(moment) =>\n          updateMoment(\n            moment.copy(\n              id = moment.id,\n              collectionId = momentData.collectionId,\n              timeslot = moment.timeslot,\n              wifi = moment.wifi,\n              headphone = moment.headphone,\n              momentType = moment.momentType\n            ))\n        case None => addMoment(momentData)\n      }\n\n      for {\n        moments <- fetchMoments\n        _       <- createOrUpdate(moments find (_.momentType == momentData.momentType))\n      } yield ()\n    }\n\n    val tasks = moments map (r => createOrUpdateMoment(r).value)\n\n    TaskService(Task.gatherUnordered(tasks) map (_ => Right((): Unit)))\n\n  }\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/impl/DockAppPersistenceServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{DockApp, DockAppData}\nimport cards.nine.repository.provider.DockAppEntity\nimport cards.nine.services.persistence._\nimport cards.nine.services.persistence.conversions.Conversions\n\ntrait DockAppPersistenceServicesImpl extends PersistenceServices {\n\n  self: Conversions with PersistenceDependencies with ImplicitsPersistenceServiceExceptions =>\n\n  def createOrUpdateDockApp(dockAppDataSeq: Seq[DockAppData]) =\n    (for {\n      fetchedDockApps <- dockAppRepository.fetchDockApps(where =\n        s\"${DockAppEntity.position} IN (${dockAppDataSeq.map(_.position).mkString(\"\\\"\", \",\", \"\\\"\")})\")\n      items = dockAppDataSeq map { dockAppData =>\n        fetchedDockApps.find(_.data.position == dockAppData.position) map { dockApp =>\n          (dockAppData, Some(dockApp.id))\n        } getOrElse {\n          (dockAppData, None)\n        }\n      }\n      (toAdd, toUpdate) = items.partition(_._2.isEmpty)\n      addedDockapps <- dockAppRepository.addDockApps(\n        toAdd.map(req => toRepositoryDockAppData(req._1)))\n      toUpdateDockApps = toUpdate.flatMap(req =>\n        req._2 map (id => toRepositoryDockApp(id, req._1)))\n      _ <- dockAppRepository.updateDockApps(toUpdateDockApps)\n    } yield (addedDockapps ++ toUpdateDockApps) map toDockApp).resolve[PersistenceServiceException]\n\n  def deleteAllDockApps() =\n    (for {\n      deleted <- dockAppRepository.deleteDockApps()\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def deleteDockAppByPosition(position: Int) =\n    (for {\n      _ <- dockAppRepository.deleteDockApps(where = s\"${DockAppEntity.position} = $position\")\n    } yield ()).resolve[PersistenceServiceException]\n\n  def deleteDockApp(dockApp: DockApp) =\n    (for {\n      deleted <- dockAppRepository.deleteDockApp(toRepositoryDockApp(dockApp))\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def fetchDockApps =\n    (for {\n      dockAppItems <- dockAppRepository.fetchDockApps()\n    } yield dockAppItems map toDockApp).resolve[PersistenceServiceException]\n\n  def findDockAppById(dockAppId: Int) =\n    (for {\n      maybeDockApp <- dockAppRepository.findDockAppById(dockAppId)\n    } yield maybeDockApp map toDockApp).resolve[PersistenceServiceException]\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/impl/MomentPersistenceServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.types.NineCardsMoment\nimport cards.nine.models.{Moment, MomentData, WidgetData}\nimport cards.nine.repository.RepositoryException\nimport cards.nine.repository.model.{Moment => RepositoryMoment}\nimport cards.nine.repository.provider.MomentEntity\nimport cards.nine.services.persistence._\nimport cards.nine.services.persistence.conversions.Conversions\n\ntrait MomentPersistenceServicesImpl extends PersistenceServices {\n\n  self: Conversions\n    with PersistenceDependencies\n    with WidgetPersistenceServicesImpl\n    with ImplicitsPersistenceServiceExceptions =>\n\n  def addMoment(momentData: MomentData) =\n    (for {\n      moment <- momentRepository.addMoment(toRepositoryMomentData(momentData))\n      _ <- addWidgets(\n        getWidgets(momentData.widgets) map (widget => widget.copy(momentId = moment.id)))\n    } yield toMoment(moment)).resolve[PersistenceServiceException]\n\n  private[this] def getWidgets(maybeWidgets: Option[Seq[WidgetData]]) =\n    maybeWidgets match {\n      case Some(widgets) => widgets\n      case None          => Seq.empty\n    }\n\n  def addMoments(moments: Seq[MomentData]) = {\n    val widgetsData = moments map (moment => getWidgets(moment.widgets))\n    (for {\n      momentsAdded <- momentRepository.addMoments(moments map toRepositoryMomentData)\n      widgets = momentsAdded.zip(widgetsData) flatMap {\n        case (moment, widgetRequest) =>\n          widgetRequest map (widget => widget.copy(momentId = moment.id))\n      }\n      _ <- addWidgets(widgets)\n    } yield momentsAdded map toMoment).resolve[PersistenceServiceException]\n  }\n\n  def deleteAllMoments() =\n    (for {\n      deleted <- momentRepository.deleteMoments()\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def deleteMoment(momentId: Int): TaskService[Int] =\n    (for {\n      deleted <- momentRepository.deleteMoment(momentId)\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def fetchMoments =\n    (for {\n      momentItems <- momentRepository.fetchMoments()\n    } yield momentItems map toMoment).resolve[PersistenceServiceException]\n\n  def findMomentById(momentId: Int) =\n    (for {\n      maybeMoment <- momentRepository.findMomentById(momentId)\n    } yield maybeMoment map toMoment).resolve[PersistenceServiceException]\n\n  def getMomentByCollectionId(collectionId: Int) =\n    (for {\n      maybeMoment <- momentRepository.fetchMomentByCollectionId(collectionId)\n    } yield maybeMoment map toMoment).resolve[PersistenceServiceException]\n\n  def getMomentByType(momentType: NineCardsMoment) = {\n\n    def readFirstMoment(moments: Seq[RepositoryMoment]): TaskService[Moment] =\n      moments.headOption match {\n        case Some(m) => TaskService.right(toMoment(m))\n        case _       => TaskService.left(RepositoryException(\"Moment not found\"))\n      }\n\n    (for {\n      moments <- momentRepository.fetchMoments(\n        s\"${MomentEntity.momentType} = ?\",\n        Seq(momentType.name))\n      moment <- readFirstMoment(moments)\n    } yield Option(moment).getOrElse(throw new RuntimeException(\"\")))\n      .resolve[PersistenceServiceException]\n  }\n\n  def fetchMomentByType(momentType: String) =\n    (for {\n      moments <- momentRepository.fetchMoments(s\"${MomentEntity.momentType} = ?\", Seq(momentType))\n    } yield moments.headOption map toMoment).resolve[PersistenceServiceException]\n\n  def fetchMomentById(momentId: Int): TaskService[Option[Moment]] =\n    (for {\n      moment <- momentRepository.findMomentById(momentId)\n    } yield moment map toMoment).resolve[PersistenceServiceException]\n\n  def updateMoment(moment: Moment) =\n    (for {\n      updated <- momentRepository.updateMoment(toRepositoryMoment(moment))\n    } yield updated).resolve[PersistenceServiceException]\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/impl/PersistenceDependencies.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.repository.repositories._\n\ntrait PersistenceDependencies {\n  val appRepository: AppRepository\n  val cardRepository: CardRepository\n  val collectionRepository: CollectionRepository\n  val dockAppRepository: DockAppRepository\n  val momentRepository: MomentRepository\n  val userRepository: UserRepository\n  val widgetRepository: WidgetRepository\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/impl/PersistenceServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.repository.repositories._\nimport cards.nine.services.persistence._\nimport cards.nine.services.persistence.conversions.Conversions\n\nclass PersistenceServicesImpl(\n    val appRepository: AppRepository,\n    val cardRepository: CardRepository,\n    val collectionRepository: CollectionRepository,\n    val dockAppRepository: DockAppRepository,\n    val momentRepository: MomentRepository,\n    val userRepository: UserRepository,\n    val widgetRepository: WidgetRepository)\n    extends PersistenceServices\n    with Conversions\n    with PersistenceDependencies\n    with AppPersistenceServicesImpl\n    with CardPersistenceServicesImpl\n    with CollectionPersistenceServicesImpl\n    with DockAppPersistenceServicesImpl\n    with MomentPersistenceServicesImpl\n    with UserPersistenceServicesImpl\n    with WidgetPersistenceServicesImpl\n    with AndroidPersistenceServicesImpl\n    with ImplicitsPersistenceServiceExceptions\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/impl/UserPersistenceServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.models.{User, UserData}\nimport cards.nine.services.persistence._\nimport cards.nine.services.persistence.conversions.Conversions\n\ntrait UserPersistenceServicesImpl extends PersistenceServices {\n\n  self: Conversions with PersistenceDependencies with ImplicitsPersistenceServiceExceptions =>\n\n  def addUser(user: UserData) =\n    (for {\n      user <- userRepository.addUser(toRepositoryUserData(user))\n    } yield toUser(user)).resolve[PersistenceServiceException]\n\n  def deleteAllUsers() =\n    (for {\n      deleted <- userRepository.deleteUsers()\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def deleteUser(user: User) =\n    (for {\n      deleted <- userRepository.deleteUser(toRepositoryUser(user))\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def fetchUsers =\n    (for {\n      userItems <- userRepository.fetchUsers\n    } yield userItems map toUser).resolve[PersistenceServiceException]\n\n  def findUserById(userId: Int) =\n    (for {\n      maybeUser <- userRepository.findUserById(userId)\n    } yield maybeUser map toUser).resolve[PersistenceServiceException]\n\n  def updateUser(user: User) =\n    (for {\n      updated <- userRepository.updateUser(toRepositoryUser(user))\n    } yield updated).resolve[PersistenceServiceException]\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/persistence/impl/WidgetPersistenceServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.NineCardExtensions._\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.{Widget, WidgetData}\nimport cards.nine.repository.provider.WidgetEntity\nimport cards.nine.services.persistence._\nimport cards.nine.services.persistence.conversions.Conversions\n\ntrait WidgetPersistenceServicesImpl extends PersistenceServices {\n\n  self: Conversions with PersistenceDependencies with ImplicitsPersistenceServiceExceptions =>\n\n  def addWidget(widget: WidgetData) =\n    (for {\n      widgetAdded <- widgetRepository.addWidget(toRepositoryWidgetData(widget))\n    } yield toWidget(widgetAdded)).resolve[PersistenceServiceException]\n\n  def addWidgets(widgets: Seq[WidgetData]) =\n    (for {\n      widgetsAdded <- widgetRepository.addWidgets(widgets map toRepositoryWidgetData)\n    } yield widgetsAdded map toWidget).resolve[PersistenceServiceException]\n\n  def deleteAllWidgets() =\n    (for {\n      deleted <- widgetRepository.deleteWidgets()\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def deleteWidget(widget: Widget) =\n    (for {\n      deleted <- widgetRepository.deleteWidget(toRepositoryWidget(widget))\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def deleteWidgetsByMoment(momentId: Int) =\n    (for {\n      deleted <- widgetRepository.deleteWidgets(where = s\"${WidgetEntity.momentId} = $momentId\")\n    } yield deleted).resolve[PersistenceServiceException]\n\n  def fetchWidgetByAppWidgetId(appWidgetId: Int) =\n    (for {\n      widget <- widgetRepository.fetchWidgetByAppWidgetId(appWidgetId)\n    } yield widget map toWidget).resolve[PersistenceServiceException]\n\n  def fetchWidgetsByMoment(momentId: Int) =\n    (for {\n      widgets <- widgetRepository.fetchWidgetsByMoment(momentId)\n    } yield widgets map toWidget).resolve[PersistenceServiceException]\n\n  def fetchWidgets() =\n    (for {\n      widgetItems <- widgetRepository.fetchWidgets()\n    } yield widgetItems map toWidget).resolve[PersistenceServiceException]\n\n  def findWidgetById(widgetId: Int) =\n    (for {\n      maybeWidget <- widgetRepository.findWidgetById(widgetId)\n    } yield maybeWidget map toWidget).resolve[PersistenceServiceException]\n\n  def updateWidget(widget: Widget) =\n    (for {\n      updated <- widgetRepository.updateWidget(toRepositoryWidget(widget))\n    } yield updated).resolve[PersistenceServiceException]\n\n  def updateWidgets(widgets: Seq[Widget]) =\n    (for {\n      updated <- widgetRepository.updateWidgets(widgets map toRepositoryWidget)\n    } yield updated).resolve[PersistenceServiceException]\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/shortcuts/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.shortcuts\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class ShortcutServicesException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsShortcutsExceptions {\n  implicit def shortcutServicesException =\n    (t: Throwable) => ShortcutServicesException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/shortcuts/ShortCutsServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.shortcuts\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.Shortcut\n\ntrait ShortcutsServices {\n\n  /**\n   * Get the applications that contains shortcuts to perform specific functions within an app\n   * @return the Seq[cards.nine.models.Shortcut] contains\n   *         information about shortcut for install it, get the icon, etc\n   * @throws ShortcutServicesException if exist some problem to get the shortcuts in the cell phone\n   */\n  def getShortcuts(implicit context: ContextSupport): TaskService[Seq[Shortcut]]\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/shortcuts/impl/ShortCutsServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.shortcuts.impl\n\nimport android.content.{ComponentName, Intent}\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.models.Shortcut\nimport cards.nine.services.shortcuts.{\n  ImplicitsShortcutsExceptions,\n  ShortcutServicesException,\n  ShortcutsServices\n}\n\nimport scala.collection.JavaConversions._\nimport scala.util.{Failure, Success, Try}\n\nclass ShortcutsServicesImpl extends ShortcutsServices with ImplicitsShortcutsExceptions {\n\n  override def getShortcuts(implicit context: ContextSupport) =\n    TaskService {\n      CatchAll[ShortcutServicesException] {\n        val packageManager = context.getPackageManager\n\n        val shortcuts =\n          packageManager.queryIntentActivities(new Intent(Intent.ACTION_CREATE_SHORTCUT), 0).toSeq\n\n        shortcuts map { resolveInfo =>\n          val activityInfo = resolveInfo.activityInfo\n          val componentName =\n            new ComponentName(activityInfo.applicationInfo.packageName, activityInfo.name)\n          val drawable = Try(context.getPackageManager.getActivityIcon(componentName)) match {\n            case Success(result) => Option(result)\n            case Failure(e)      => None\n          }\n          val intent = new Intent(Intent.ACTION_CREATE_SHORTCUT)\n          intent.addCategory(Intent.CATEGORY_DEFAULT)\n          intent.setComponent(componentName)\n          Shortcut(\n            title = resolveInfo.loadLabel(packageManager).toString,\n            icon = drawable,\n            intent = intent)\n        } sortBy (_.title)\n      }\n    }\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/track/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.track\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class TrackServicesException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n\n  cause foreach initCause\n\n}\n\ntrait ImplicitsTrackServicesException {\n  implicit def trackServicesExceptionConverter =\n    (t: Throwable) => TrackServicesException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/track/TrackServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.track\n\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.TrackEvent\n\ntrait TrackServices {\n\n  /**\n   * Track event in Google Analytics\n   *\n   * @throws TrackServicesException if there was an error with the request GoogleAnalytics\n   */\n  def trackEvent(event: TrackEvent): TaskService[Unit]\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/track/impl/ConsoleTrackServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.track.impl\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types.MomentCategory\nimport cards.nine.services.track.TrackServices\nimport monix.eval.Task\n\nclass ConsoleTrackServices extends TrackServices {\n\n  override def trackEvent(event: TrackEvent): TaskService[Unit] = TaskService {\n    val categoryName = event.category match {\n      case MomentCategory(moment) => s\"WIDGET_${moment.name}\"\n      case _                      => event.category.name\n    }\n\n    Task(Right(println(s\"\"\"Track\n          | Action ${event.action.name}\n          | Category $categoryName\n          | Label ${event.label.getOrElse(\"\")}\n          | Screen ${event.screen.name}\n          | Value ${event.value.map(_.value).getOrElse(0)}\"\"\".stripMargin)))\n  }\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/track/impl/DisableTrackServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.track.impl\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.models.TrackEvent\nimport cards.nine.models.types.MomentCategory\nimport cards.nine.services.track.TrackServices\nimport monix.eval.Task\n\nclass DisableTrackServices extends TrackServices {\n\n  override def trackEvent(event: TrackEvent): TaskService[Unit] = TaskService {\n    val categoryName = event.category match {\n      case MomentCategory(moment) => s\"WIDGET_${moment.name}\"\n      case _                      => event.category.name\n    }\n\n    Task(Right(println(s\"\"\"Event no tracked\n                          | Action ${event.action.name}\n                          | Category $categoryName\n                          | Label ${event.label.getOrElse(\"\")}\n                          | Screen ${event.screen.name}\n                          | Value ${event.value.map(_.value).getOrElse(0)}\"\"\".stripMargin)))\n  }\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/utils/ResourceUtils.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.utils\n\nimport cards.nine.commons.contexts.ContextSupport\n\nclass ResourceUtils {\n\n  def getPath(filename: String)(implicit context: ContextSupport): String =\n    s\"${context.getAppIconsDir.getPath}/$filename\"\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/widgets/Exceptions.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.widgets\n\nimport cards.nine.commons.services.TaskService.NineCardException\n\ncase class WidgetServicesException(message: String, cause: Option[Throwable] = None)\n    extends RuntimeException(message)\n    with NineCardException {\n  cause map initCause\n}\n\ntrait ImplicitsWidgetsExceptions {\n  implicit def widgetServicesException =\n    (t: Throwable) => WidgetServicesException(t.getMessage, Option(t))\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/widgets/WidgetsServices.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.widgets\n\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService.TaskService\nimport cards.nine.models.AppWidget\n\ntrait WidgetsServices {\n\n  /**\n   * Get the available widgets in the system\n   * @return the Seq[cards.nine.models.AppWidget] contains information about the widget\n   * @throws WidgetServicesException if exist some problem to get the widgets in the cell phone\n   */\n  def getWidgets(implicit context: ContextSupport): TaskService[Seq[AppWidget]]\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/widgets/impl/WidgetsServicesImpl.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.widgets.impl\n\nimport android.os.Build\nimport cards.nine.commons.CatchAll\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.models.Conversions\nimport cards.nine.services.widgets.utils.AppWidgetManagerCompat\nimport cards.nine.services.widgets.utils.impl.{\n  AppWidgetManagerImplDefault,\n  AppWidgetManagerImplLollipop\n}\nimport cards.nine.services.widgets.{\n  ImplicitsWidgetsExceptions,\n  WidgetServicesException,\n  WidgetsServices\n}\n\nclass WidgetsServicesImpl extends WidgetsServices with ImplicitsWidgetsExceptions {\n\n  override def getWidgets(implicit context: ContextSupport) =\n    TaskService {\n      CatchAll[WidgetServicesException] {\n        val appWidgetManager = getAppWidgetManager\n        appWidgetManager.getAllProviders\n\n      }\n    }\n\n  protected def getAppWidgetManager(\n      implicit context: ContextSupport): AppWidgetManagerCompat with Conversions = {\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) new AppWidgetManagerImplLollipop\n    else new AppWidgetManagerImplDefault\n  }\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/widgets/utils/AppWidgetManagerCompat.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.widgets.utils\n\nimport cards.nine.models.AppWidget\n\ntrait AppWidgetManagerCompat {\n\n  def getAllProviders: Seq[AppWidget]\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/widgets/utils/impl/AppWidgetManagerImplDefault.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.widgets.utils.impl\n\nimport android.appwidget.{AppWidgetManager, AppWidgetProviderInfo}\nimport android.content.pm.PackageManager\nimport android.os.Build\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.models.Conversions\nimport cards.nine.services.widgets.utils.AppWidgetManagerCompat\n\nimport scala.collection.JavaConversions._\n\nclass AppWidgetManagerImplDefault(implicit contextSupport: ContextSupport)\n    extends AppWidgetManagerCompat\n    with Conversions {\n\n  lazy val packageManager: PackageManager = contextSupport.getPackageManager\n\n  override def getAllProviders = getAppWidgetProviderInfo map { appWidgetProviderInfo =>\n    val label        = getLabel(appWidgetProviderInfo)\n    val userHashCode = getUser(appWidgetProviderInfo)\n    toWidget(appWidgetProviderInfo, label, userHashCode)\n  }\n\n  protected def getAppWidgetProviderInfo =\n    AppWidgetManager.getInstance(contextSupport.context).getInstalledProviders.toSeq\n\n  protected def getLabel(info: AppWidgetProviderInfo) = info.label.trim\n\n  protected def getUser(info: AppWidgetProviderInfo) =\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)\n      Option(android.os.Process.myUserHandle.hashCode)\n    else None\n\n}\n"
  },
  {
    "path": "modules/services/src/main/scala/cards/nine/services/widgets/utils/impl/AppWidgetManagerImplLollipop.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.widgets.utils.impl\n\nimport android.annotation.SuppressLint\nimport android.appwidget.{AppWidgetManager, AppWidgetProviderInfo}\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.os.{UserHandle, UserManager}\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.models.{AppWidget, Conversions}\nimport cards.nine.services.widgets.utils.AppWidgetManagerCompat\n\nimport scala.collection.JavaConversions._\n\nclass AppWidgetManagerImplLollipop(implicit contextSupport: ContextSupport)\n    extends AppWidgetManagerCompat\n    with Conversions {\n\n  lazy val packageManager: PackageManager = contextSupport.getPackageManager\n\n  override def getAllProviders: Seq[AppWidget] = {\n    for {\n      userHandle            <- getUserHandle\n      appWidgetProviderInfo <- getAppWidgetProviderInfo(userHandle)\n    } yield {\n      val label        = getLabel(appWidgetProviderInfo)\n      val userHashCode = getUser(appWidgetProviderInfo)\n      toWidget(appWidgetProviderInfo, label, userHashCode)\n    }\n  }\n\n  @SuppressLint(Array(\"NewApi\"))\n  protected def getUserHandle =\n    contextSupport.context\n      .getSystemService(Context.USER_SERVICE)\n      .asInstanceOf[UserManager]\n      .getUserProfiles\n      .toSeq\n\n  @SuppressLint(Array(\"NewApi\"))\n  protected def getAppWidgetProviderInfo(userHandle: UserHandle) =\n    AppWidgetManager\n      .getInstance(contextSupport.context)\n      .getInstalledProvidersForProfile(userHandle)\n      .toSeq\n\n  @SuppressLint(Array(\"NewApi\"))\n  protected def getLabel(implicit info: AppWidgetProviderInfo) = info.loadLabel(packageManager)\n\n  @SuppressLint(Array(\"NewApi\"))\n  protected def getUser(implicit info: AppWidgetProviderInfo) =\n    Option(android.os.Process.myUserHandle.hashCode)\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/api/impl/ApiServicesImplData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.api.impl\n\nimport cards.nine.api.version1.{User, _}\nimport cards.nine.api.version2._\nimport cards.nine.commons.test.data.ApiTestData\nimport cards.nine.commons.test.data.ApiV1Values._\nimport cards.nine.commons.test.data.ApiValues._\nimport cards.nine.commons.test.data.ApplicationValues._\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.MomentValues._\nimport cards.nine.commons.test.data.SharedCollectionValues._\nimport cards.nine.commons.test.data.UserV1Values._\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.models.NineCardsIntentConversions\nimport cards.nine.models.types.NineCardsMoment\nimport play.api.libs.json.Json\n\ntrait ApiServicesImplData extends ApiTestData with NineCardsIntentConversions {\n\n  def authGoogleDevice(num: Int = 0) =\n    AuthGoogleDevice(\n      name = userDeviceName + num,\n      deviceId = userDeviceId + num,\n      secretToken = marketToken,\n      permissions = permissions)\n\n  val authGoogleDevice: AuthGoogleDevice = authGoogleDevice(0)\n  val seqAuthGoogleDevice: Seq[AuthGoogleDevice] =\n    Seq(authGoogleDevice(0), authGoogleDevice(1), authGoogleDevice(2))\n\n  def authGoogle(num: Int = 0) = AuthGoogle(email = email, devices = seqAuthGoogleDevice)\n\n  val authGoogle: AuthGoogle = authGoogle(0)\n\n  def authFacebook(num: Int = 0) =\n    AuthFacebook(\n      id = authFacebookId,\n      accessToken = authFacebookAccessToken,\n      expirationDate = authFacebookExpirationDate)\n\n  val authFacebook: AuthFacebook = authFacebook(0)\n\n  def authTwitter(num: Int = 0) =\n    AuthTwitter(\n      id = authTwitterId,\n      screenName = authTwitterScreenName,\n      consumerKey = authTwitterConsumerKey,\n      consumerSecret = authTwitterConsumerSecret,\n      authToken = authTwitterAuthToken,\n      authTokenSecret = authTwitterAuthTokenSecret,\n      key = authTwitterKey,\n      secretKey = authTwitterSecretKey)\n\n  val authTwitter: AuthTwitter = authTwitter(0)\n\n  def authAnonymous(num: Int = 0) = AuthAnonymous(id = authAnonymousId)\n\n  val authAnonymous: AuthAnonymous = authAnonymous(0)\n\n  def authData(num: Int = 0) =\n    AuthData(\n      google = Some(authGoogle),\n      facebook = Option(authFacebook),\n      twitter = Option(authTwitter),\n      anonymous = Option(authAnonymous))\n\n  val authData: AuthData = authData(0)\n\n  def apiUserV1(num: Int = 0) =\n    User(\n      _id = Option(userId.toString),\n      sessionToken = Option(sessionToken),\n      email = Option(email),\n      username = Option(userV1Name),\n      password = Option(userV1Password),\n      authData = Option(authData))\n\n  val apiUserV1: User = apiUserV1(0)\n\n  val loginV1User = User(\n    _id = None,\n    email = None,\n    sessionToken = None,\n    username = None,\n    password = None,\n    authData = Option(\n      authData.copy(\n        google = Option(authGoogle.copy(devices = Seq(authGoogleDevice))),\n        facebook = None,\n        twitter = None,\n        anonymous = None)))\n\n  def userConfigProfileImage =\n    UserConfigProfileImage(\n      imageType = userConfigPlusImageType,\n      imageUrl = imageUrl,\n      secureUrl = Option(userConfigPlusSecureUrl))\n\n  def userConfigPlusProfile =\n    UserConfigPlusProfile(displayName = displayName, profileImage = userConfigProfileImage)\n\n  def userConfigCollectionItem(num: Int = 0) =\n    UserConfigCollectionItem(\n      itemType = itemType.name,\n      title = title + num,\n      metadata = Json.parse(intent),\n      categories = Option(Seq(category.name, anotherCategory.name)))\n\n  val seqUserConfigCollectionItem: Seq[UserConfigCollectionItem] =\n    Seq(userConfigCollectionItem(0), userConfigCollectionItem(1), userConfigCollectionItem(2))\n\n  def userConfigCollection(num: Int = 0) =\n    UserConfigCollection(\n      name = collectionName,\n      originalSharedCollectionId = Option(originalSharedCollectionId),\n      sharedCollectionId = Option(sharedCollectionId),\n      sharedCollectionSubscribed = Option(sharedCollectionSubscribed),\n      items = seqUserConfigCollectionItem,\n      collectionType = collectionType.name,\n      constrains = constrains,\n      wifi = wifiSeq,\n      occurrence = occurrence,\n      icon = apiV1CollectionIcon,\n      radius = radius,\n      lat = latitude,\n      lng = longitude,\n      alt = altitude,\n      category = Option(category.name))\n\n  val seqUserConfigCollection: Seq[UserConfigCollection] =\n    Seq(userConfigCollection(0), userConfigCollection(1), userConfigCollection(2))\n\n  def userConfigDevice(num: Int = 0) =\n    UserConfigDevice(\n      deviceId = deviceIdPrefix + num,\n      deviceName = userDeviceName + num,\n      collections = seqUserConfigCollection)\n\n  val seqUserConfigDevice: Seq[UserConfigDevice] =\n    Seq(userConfigDevice(0), userConfigDevice(1), userConfigDevice(2))\n\n  def userConfigGeoInfo =\n    UserConfigGeoInfo(homeMorning = None, homeNight = None, work = None, current = None)\n\n  def userConfigStatusInfo =\n    UserConfigStatusInfo(\n      products = products,\n      friendsReferred = friendsReferred,\n      themesShared = themesShared,\n      collectionsShared = collectionsShared,\n      customCollections = customCollections,\n      earlyAdopter = earlyAdopter,\n      communityMember = communityMember,\n      joinedThrough = joinedThrough,\n      tester = tester)\n\n  def userConfig(num: Int = 0) =\n    UserConfig(\n      _id = userV1Id,\n      email = email,\n      plusProfile = userConfigPlusProfile,\n      devices = seqUserConfigDevice,\n      geoInfo = userConfigGeoInfo,\n      status = userConfigStatusInfo)\n\n  val userConfig: UserConfig = userConfig(0)\n\n  def categorizedApp(num: Int = 0) =\n    CategorizedApp(packageName = applicationPackageName + num, categories = Seq(categoryStr))\n\n  val categorizedApp: CategorizedApp = categorizedApp(0)\n  val seqCategorizedApp: Seq[CategorizedApp] =\n    Seq(categorizedApp(0), categorizedApp(1), categorizedApp(2))\n\n  def categorizedAppDetail(num: Int = 0) =\n    CategorizedAppDetail(\n      packageName = applicationPackageName + num,\n      title = applicationName + num,\n      categories = Seq(categoryStr),\n      icon = apiIcon,\n      free = free,\n      downloads = downloads,\n      stars = stars)\n\n  val categorizedAppDetail: CategorizedAppDetail = categorizedAppDetail(0)\n  val seqCategorizedAppDetail: Seq[CategorizedAppDetail] =\n    Seq(categorizedAppDetail(0), categorizedAppDetail(1), categorizedAppDetail(2))\n\n  def screenshots(num: Int = 0) = screenshot + num\n\n  val seqScreenshots: Seq[String] = Seq(screenshots(0), screenshots(1), screenshots(2))\n\n  def notCategorizedApp(num: Int = 0) =\n    NotCategorizedApp(\n      packageName = sharedCollectionPackageName + num,\n      title = sharedCollectionPackageTitle + num,\n      downloads = sharedCollectionDownloads,\n      icon = sharedCollectionPackageIcon,\n      stars = sharedCollectionPackageStars,\n      free = free,\n      screenshots = seqScreenshots)\n\n  val notCategorizedApp: NotCategorizedApp = notCategorizedApp(0)\n  val seqNotCategorizedApp: Seq[NotCategorizedApp] =\n    Seq(notCategorizedApp(0), notCategorizedApp(1), notCategorizedApp(2))\n\n  val packageStats = PackagesStats(1, None)\n\n  def collectionApp(num: Int = 0) =\n    CollectionApp(\n      stars = sharedCollectionPackageStars,\n      icon = sharedCollectionPackageIcon,\n      packageName = sharedCollectionPackageName + num,\n      downloads = sharedCollectionDownloads,\n      categories = Seq(category.name),\n      title = sharedCollectionPackageTitle + num,\n      free = sharedCollectionFree)\n\n  val collectionApp: CollectionApp = collectionApp(0)\n  val seqCollectionApp: Seq[CollectionApp] =\n    Seq(collectionApp(0), collectionApp(1), collectionApp(2))\n\n  def collectionV2(num: Int = 0) =\n    Collection(\n      name = sharedCollectionName,\n      author = author,\n      owned = owned,\n      icon = sharedCollectionIcon,\n      category = category.name,\n      community = community,\n      publishedOn = publishedOnStr,\n      installations = Some(installations),\n      views = Some(views),\n      subscriptions = Some(subscriptions),\n      publicIdentifier = sharedCollectionId + num,\n      appsInfo = seqCollectionApp,\n      packages = seqCollectionApp map (_.packageName))\n\n  val collectionV2: Collection         = collectionV2(0)\n  val seqCollectionV2: Seq[Collection] = Seq(collectionV2(0), collectionV2(1), collectionV2(2))\n\n  val rankAppMap = Map(\n    seqPackagesByCategory map (packagesByCategory =>\n                                 packagesByCategory.category.name -> packagesByCategory.packages): _*)\n\n  val recommendationsResponse = RecommendationsResponse(items = seqNotCategorizedApp)\n\n  val recommendationByAppsResponse = RecommendationsByAppsResponse(apps = seqNotCategorizedApp)\n\n  val recommendationsByAppsRequest =\n    RecommendationsByAppsRequest(apiPackages, excludedPackages, limit)\n\n  val loginRequest = ApiLoginRequest(email, androidId, tokenId)\n\n  val installationRequest = InstallationRequest(deviceToken)\n\n  val categorizeOneRequest = CategorizeRequest(\n    seqCategorizedApp.headOption.map(_.packageName).toSeq)\n\n  val categorizeRequest = CategorizeRequest(seqCategorizedApp.map(_.packageName))\n\n  val recommendationsRequest = RecommendationsRequest(excludedPackages, limit)\n\n  val createCollectionRequest = CreateCollectionRequest(\n    sharedCollectionName,\n    author,\n    sharedCollectionPackageIcon,\n    categoryStr,\n    community,\n    apiPackages)\n\n  val updateCollectionRequest =\n    UpdateCollectionRequest(Some(CollectionUpdateInfo(sharedCollectionName)), Some(apiPackages))\n\n  val updateCollectionResponse = UpdateCollectionResponse(sharedCollectionId, packageStats)\n\n  val collectionsResponse = CollectionsResponse(seqCollectionV2)\n\n  val rankAppsRequest = RankAppsRequest(rankAppMap, Some(location))\n\n  val rankAppsResponse = RankAppsResponse((rankAppMap map { app =>\n    RankAppsCategoryResponse(app._1, app._2)\n  }).toSeq)\n\n  val seqSubscription = Seq(sharedCollectionId)\n\n  def rankAppsCategoryResponse(num: Int = 0) =\n    RankAppsCategoryResponse(category = momentTypeSeq(num), packages = Seq(apiPackages(num)))\n\n  val seqRankAppsCategoryResponse: Seq[RankAppsCategoryResponse] =\n    Seq(rankAppsCategoryResponse(0), rankAppsCategoryResponse(1), rankAppsCategoryResponse(2))\n\n  val rankAppsByMomentResponse = RankAppsByMomentResponse(seqRankAppsCategoryResponse)\n\n  val rankAppsByMomentRequest =\n    RankAppsByMomentRequest(apiPackages, momentTypeSeq.take(3), Some(location), limit)\n\n  def rankWidgetsResponse(num: Int = 0) =\n    RankWidgetsResponse(packageName = apiPackageName + num, className = apiClassName + num)\n\n  val seqRankWidgetsResponse: Seq[RankWidgetsResponse] =\n    Seq(rankWidgetsResponse(0), rankWidgetsResponse(1), rankWidgetsResponse(2))\n\n  def rankWidgetsWithMomentResponse(num: Int = 0) =\n    RankWidgetsWithMomentResponse(\n      moment = momentTypeSeq(num),\n      widgets = Seq(seqRankWidgetsResponse(num)))\n\n  val seqRankWidgetsWithMomentResponse: Seq[RankWidgetsWithMomentResponse] = Seq(\n    rankWidgetsWithMomentResponse(0),\n    rankWidgetsWithMomentResponse(1),\n    rankWidgetsWithMomentResponse(2))\n\n  val rankWidgetsByMomentResponse = RankWidgetsByMomentResponse(seqRankWidgetsWithMomentResponse)\n\n  val rankWidgetsByMomentRequest =\n    RankWidgetsByMomentRequest(apiPackages, momentTypeSeq.take(3), Some(location), limit)\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/api/impl/ApiServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.api.impl\n\nimport cards.nine.api._\nimport cards.nine.api.rest.client.http.HttpClientException\nimport cards.nine.api.rest.client.messages.ServiceClientResponse\nimport cards.nine.api.version2._\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.ApiValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.MomentValues._\nimport cards.nine.commons.test.data.SharedCollectionValues._\nimport cards.nine.commons.test.data.UserV1Values._\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.commons.test.data.{ApiTestData, ApiV1TestData, SharedCollectionTestData}\nimport cards.nine.models.types.NineCardsCategory\nimport cards.nine.models.{Device, _}\nimport cards.nine.services.api._\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\nimport scala.reflect.ClassTag\n\ntrait ApiServicesSpecification extends Specification with Mockito {\n\n  trait ApiServicesScope\n      extends Scope\n      with ApiServicesImplData\n      with ApiTestData\n      with ApiV1TestData\n      with SharedCollectionTestData {\n\n    val apiServicesConfig =\n      ApiServicesConfig(appId = appId, appKey = appKey, localization = location)\n\n    val serviceHeader =\n      ServiceHeader(requestConfig.apiKey, requestConfig.sessionToken, requestConfig.androidId)\n\n    val serviceMarketHeader = ServiceMarketHeader(\n      requestConfig.apiKey,\n      requestConfig.sessionToken,\n      requestConfig.androidId,\n      requestConfig.marketToken)\n\n    val apiService = mock[cards.nine.api.version2.ApiService]\n\n    val apiServiceV1 = mock[cards.nine.api.version1.ApiService]\n\n    val apiServices = new ApiServicesImpl(apiServicesConfig, apiService, apiServiceV1)\n\n    val exception = HttpClientException(\"\")\n\n    def mustLeft[T <: NineCardException](service: TaskService[_])(\n        implicit classTag: ClassTag[T]): Unit =\n      service.value.run must beLike {\n        case Left(e) => e must beAnInstanceOf[T]\n      }\n  }\n}\n\nclass ApiServicesImplSpec extends ApiServicesSpecification {\n\n  \"loginV1\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiServiceV1.baseUrl returns baseUrl\n        apiServiceV1.login(any, any)(any, any) returns\n          TaskService {\n            Task(Either.right(ServiceClientResponse(statusCode, Option(apiUserV1))))\n          }\n\n        val result = apiServices.loginV1(email, device).value.run\n        result shouldEqual Right(loginResponseV1)\n\n        there was one(apiServiceV1).login(===(loginV1User), any)(any, any)\n      }\n\n    \"return an ApiServiceV1ConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiServiceV1.baseUrl returns \"\"\n\n        mustLeft[ApiServiceV1ConfigurationException](\n          apiServices.loginV1(\"\", Device(\"\", \"\", \"\", Seq.empty)))\n      }\n\n    \"return an ApiServiceException when the service returns None\" in\n      new ApiServicesScope {\n\n        apiServiceV1.baseUrl returns baseUrl\n        apiServiceV1.login(any, any)(any, any) returns TaskService(\n          Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        mustLeft[ApiServiceException](apiServices.loginV1(\"\", Device(\"\", \"\", \"\", Seq.empty)))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiServiceV1.baseUrl returns baseUrl\n        apiServiceV1.login(any, any)(any, any) returns TaskService {\n          Task(Either.left(exception))\n        }\n\n        mustLeft[ApiServiceException](apiServices.loginV1(\"\", Device(\"\", \"\", \"\", Seq.empty)))\n      }\n\n  }\n\n  \"getUserConfigV1\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiServiceV1.baseUrl returns baseUrl\n        apiServiceV1.getUserConfig(any)(any) returns\n          TaskService {\n            Task(Either.right(ServiceClientResponse(statusCode, Some(userConfig))))\n          }\n\n        val result = apiServices.getUserConfigV1().value.run\n        result shouldEqual Right(userV1)\n      }\n\n    \"return an ApiServiceV1ConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiServiceV1.baseUrl returns \"\"\n\n        mustLeft[ApiServiceV1ConfigurationException](apiServices.getUserConfigV1())\n      }\n\n    \"return an ApiServiceException when the service returns None\" in\n      new ApiServicesScope {\n\n        apiServiceV1.baseUrl returns baseUrl\n        apiServiceV1.getUserConfig(any)(any) returns TaskService(\n          Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        mustLeft[ApiServiceException](apiServices.getUserConfigV1())\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiServiceV1.baseUrl returns baseUrl\n        apiServiceV1.getUserConfig(any)(any) returns TaskService(Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.getUserConfigV1())\n      }\n\n  }\n\n  \"login\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.login(any)(any, any) returns\n          TaskService {\n            Task(\n              Either.right(\n                ServiceClientResponse[cards.nine.api.version2.ApiLoginResponse](\n                  statusCode,\n                  Some(version2.ApiLoginResponse(apiKey, sessionToken)))))\n          }\n\n        val result = apiServices.login(email, androidId, tokenId).value.run\n        result shouldEqual Right(LoginResponse(apiKey, sessionToken))\n\n        there was one(apiService).login(===(loginRequest))(any, any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](apiServices.login(email, androidId, tokenId))\n      }\n\n    \"return an ApiServiceException when the service returns None\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.login(any)(any, any) returns\n          TaskService {\n            Task(\n              Either.right(\n                ServiceClientResponse[cards.nine.api.version2.ApiLoginResponse](statusCode, None)))\n          }\n\n        mustLeft[ApiServiceException](apiServices.login(email, androidId, tokenId))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.login(any)(any, any) returns TaskService(Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.login(email, androidId, tokenId))\n      }\n\n  }\n\n  \"updateInstallation\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.installations(any, any)(any, any) returns\n          TaskService {\n            Task(\n              Either.right(\n                ServiceClientResponse[cards.nine.api.version2.InstallationResponse](\n                  statusCode,\n                  Some(version2.InstallationResponse(androidId, deviceToken)))))\n          }\n\n        val result = apiServices.updateInstallation(Some(deviceToken)).value.run\n\n        there was one(apiService)\n          .installations(===(installationRequest), ===(serviceHeader))(any, any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](apiServices.updateInstallation(Some(\"\")))\n      }\n\n    \"return an ApiServiceException when the service returns None\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.installations(any, any)(any, any) returns\n          TaskService {\n            Task(\n              Either.right(\n                ServiceClientResponse[cards.nine.api.version2.InstallationResponse](\n                  statusCode,\n                  None)))\n          }\n\n        mustLeft[ApiServiceException](apiServices.updateInstallation(Some(\"\")))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.installations(any, any)(any, any) returns TaskService(\n          Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.updateInstallation(Some(\"\")))\n      }\n\n  }\n\n  \"googlePlayPackage\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.categorize(any, any)(any, any) returns\n          TaskService {\n            Task(\n              Either.right(\n                ServiceClientResponse(\n                  statusCode,\n                  Some(version2.CategorizeResponse(Seq.empty, seqCategorizedApp)))))\n          }\n\n        val result = apiServices.googlePlayPackage(seqCategorizedApp.head.packageName).value.run\n        result must beLike {\n          case Right(app) =>\n            Some(app) shouldEqual seqCategorizedApp.headOption.map(a =>\n              CategorizedPackage(a.packageName, a.categories.headOption.map(NineCardsCategory(_))))\n        }\n\n        there was one(apiService)\n          .categorize(===(categorizeOneRequest), ===(serviceMarketHeader))(any, any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](apiServices.googlePlayPackage(\"\"))\n      }\n\n    \"return an ApiServiceException when the service returns None\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.categorize(any, any)(any, any) returns TaskService(\n          Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        mustLeft[ApiServiceException](apiServices.googlePlayPackage(\"\"))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.categorize(any, any)(any, any) returns TaskService(Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.googlePlayPackage(\"\"))\n      }\n\n  }\n\n  \"googlePlayPackages\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.categorize(any, any)(any, any) returns\n          TaskService {\n            Task(\n              Either.right(\n                ServiceClientResponse[cards.nine.api.version2.CategorizeResponse](\n                  statusCode,\n                  Some(version2.CategorizeResponse(Seq.empty, seqCategorizedApp)))))\n          }\n\n        val result = apiServices.googlePlayPackages(seqCategorizedApp.map(_.packageName)).value.run\n        result shouldEqual Right(\n          seqCategorizedApp map (a =>\n                                   CategorizedPackage(\n                                     a.packageName,\n                                     a.categories.headOption.map(NineCardsCategory(_)))))\n\n        there was one(apiService)\n          .categorize(===(categorizeRequest), ===(serviceMarketHeader))(any, any)\n      }\n\n    \"return an empty sequence if the services returns a valid response with None\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.categorize(any, any)(any, any) returns\n          TaskService(Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        val result = apiServices.googlePlayPackages(seqCategorizedApp.map(_.packageName)).value.run\n        result must beLike {\n          case Right(packages) =>\n            packages must beEmpty\n        }\n\n        there was one(apiService)\n          .categorize(===(categorizeRequest), ===(serviceMarketHeader))(any, any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](apiServices.googlePlayPackages(Seq.empty))\n      }\n\n    \"return an empty sequence when the service returns None\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.categorize(any, any)(any, any) returns TaskService(\n          Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        val result = apiServices.googlePlayPackages(seqCategorizedApp.map(_.packageName)).value.run\n        result must beLike {\n          case Right(packages) =>\n            packages must beEmpty\n        }\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.categorize(any, any)(any, any) returns TaskService(Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.googlePlayPackages(Seq.empty))\n      }\n\n  }\n\n  \"googlePlayPackagesDetail\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.categorizeDetail(any, any)(any, any) returns\n          TaskService {\n            Task(\n              Either.right(\n                ServiceClientResponse(\n                  statusCode,\n                  Some(CategorizeDetailResponse(Seq.empty, seqCategorizedAppDetail)))))\n          }\n\n        val result =\n          apiServices.googlePlayPackagesDetail(seqCategorizedApp.map(_.packageName)).value.run\n        result shouldEqual Right(categorizedDetailPackages)\n\n        there was one(apiService)\n          .categorizeDetail(===(categorizeRequest), ===(serviceMarketHeader))(any, any)\n      }\n\n    \"return an empty sequence if the services returns a valid response with None\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.categorizeDetail(any, any)(any, any) returns\n          TaskService {\n            Task(Either.right(ServiceClientResponse[CategorizeDetailResponse](statusCode, None)))\n          }\n\n        val result =\n          apiServices.googlePlayPackagesDetail(seqCategorizedApp.map(_.packageName)).value.run\n        result must beLike {\n          case Right(packages) =>\n            packages must beEmpty\n        }\n\n        there was one(apiService)\n          .categorizeDetail(===(categorizeRequest), ===(serviceMarketHeader))(any, any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](apiServices.googlePlayPackagesDetail(Seq.empty))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.categorizeDetail(any, any)(any, any) returns TaskService(\n          Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.googlePlayPackagesDetail(Seq.empty))\n      }\n\n  }\n\n  \"getRecommendedApps\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.recommendations(any, any, any, any)(any, any) returns\n          TaskService {\n            Task(Either.right(ServiceClientResponse(statusCode, Some(recommendationsResponse))))\n          }\n\n        val result = apiServices.getRecommendedApps(categoryStr, excludedPackages, limit).value.run\n        result must beLike {\n          case Right(recommendedApps) =>\n            recommendedApps.map(_.packageName) shouldEqual seqNotCategorizedApp.map(_.packageName)\n        }\n\n        there was one(apiService).recommendations(\n          ===(categoryStr),\n          any,\n          ===(recommendationsRequest),\n          ===(serviceMarketHeader))(any, any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](\n          apiServices.getRecommendedApps(categoryStr, Seq.empty, limit))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.recommendations(any, any, any, any)(any, any) returns TaskService(\n          Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](\n          apiServices.getRecommendedApps(categoryStr, Seq.empty, limit))\n      }\n\n  }\n\n  \"getRecommendedAppsByPackage\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.recommendationsByApps(any, any)(any, any) returns\n          TaskService(\n            Task(\n              Either.right(ServiceClientResponse(statusCode, Some(recommendationByAppsResponse)))))\n\n        val result =\n          apiServices.getRecommendedAppsByPackages(apiPackages, excludedPackages, limit).value.run\n        result must beLike {\n          case Right(recommendedApps) =>\n            recommendedApps.map(_.packageName) shouldEqual seqNotCategorizedApp.map(_.packageName)\n        }\n\n        there was one(apiService).recommendationsByApps(\n          ===(recommendationsByAppsRequest),\n          ===(serviceMarketHeader))(any, any)\n      }\n\n    \"return an empty sequence if the services returns None\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.recommendationsByApps(any, any)(any, any) returns\n          TaskService(Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        val result =\n          apiServices.getRecommendedAppsByPackages(apiPackages, excludedPackages, limit).value.run\n        result must beLike {\n          case Right(recommendedApps) =>\n            recommendedApps must beEmpty\n        }\n\n        there was one(apiService).recommendationsByApps(\n          ===(recommendationsByAppsRequest),\n          ===(serviceMarketHeader))(any, any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](\n          apiServices.getRecommendedAppsByPackages(apiPackages, Seq.empty, limit))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.recommendationsByApps(any, any)(any, any) returns TaskService(\n          Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](\n          apiServices.getRecommendedAppsByPackages(apiPackages, Seq.empty, limit))\n      }\n\n  }\n\n  \"getSharedCollection\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.getCollection(any, any)(any) returns\n          TaskService(Task(Either.right(ServiceClientResponse(statusCode, Option(collectionV2)))))\n\n        val result = apiServices.getSharedCollection(sharedCollectionId).value.run\n        result shouldEqual Right(sharedCollection)\n\n        there was one(apiService).getCollection(===(sharedCollectionId), ===(serviceMarketHeader))(\n          any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](\n          apiServices.getSharedCollection(sharedCollectionId))\n      }\n\n    \"return an ApiServiceException when the service returns None\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.getCollection(any, any)(any) returns\n          TaskService(Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        mustLeft[ApiServiceException](apiServices.getSharedCollection(sharedCollectionId))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.getCollection(any, any)(any) returns TaskService(Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.getSharedCollection(sharedCollectionId))\n      }\n\n  }\n\n  \"getSharedCollectionsByCategory\" should {\n\n    \"return a valid response if the services returns a valid response for TOP apps\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.topCollections(any, any, any, any)(any) returns\n          TaskService {\n            Task(Either.right(ServiceClientResponse(statusCode, Some(collectionsResponse))))\n          }\n\n        val result = apiServices\n          .getSharedCollectionsByCategory(categoryStr, collectionTypeTop, offset, limit)\n          .value\n          .run\n        result must beLike {\n          case Right(shareCollections) =>\n            shareCollections.size shouldEqual seqCollection.size\n            shareCollections map (s =>\n                                    Option(s.sharedCollectionId)) shouldEqual (seqCollection map (_.sharedCollectionId))\n        }\n\n        there was one(apiService)\n          .topCollections(===(categoryStr), ===(offset), ===(limit), ===(serviceMarketHeader))(any)\n      }\n\n    \"return an ApiServiceException when the service returns an exception for TOP apps\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.topCollections(any, any, any, any)(any) returns TaskService(\n          Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](\n          apiServices\n            .getSharedCollectionsByCategory(categoryStr, collectionTypeTop, offset, limit))\n      }\n\n    \"return a valid response if the services returns a valid response for LATEST apps\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.latestCollections(any, any, any, any)(any) returns\n          TaskService(\n            Task(Either.right(ServiceClientResponse(statusCode, Some(collectionsResponse)))))\n\n        val result = apiServices\n          .getSharedCollectionsByCategory(categoryStr, collectionTypeLatest, offset, limit)\n          .value\n          .run\n        result must beLike {\n          case Right(shareCollections) =>\n            shareCollections.size shouldEqual seqCollection.size\n            shareCollections map (s =>\n                                    Option(s.sharedCollectionId)) shouldEqual (seqCollection map (_.sharedCollectionId))\n        }\n\n        there was one(apiService).latestCollections(\n          ===(categoryStr),\n          ===(offset),\n          ===(limit),\n          ===(serviceMarketHeader))(any)\n      }\n\n    \"return an ApiServiceException when the service returns an exception for LATEST apps\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.latestCollections(any, any, any, any)(any) returns TaskService(\n          Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](\n          apiServices\n            .getSharedCollectionsByCategory(categoryStr, collectionTypeLatest, offset, limit))\n      }\n\n    \"return an ApiServiceException for an invalid collection type\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n\n        mustLeft[ApiServiceException](\n          apiServices\n            .getSharedCollectionsByCategory(categoryStr, collectionTypeUnknown, offset, limit))\n\n        there was no(apiService).latestCollections(any, any, any, any)(any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](\n          apiServices\n            .getSharedCollectionsByCategory(categoryStr, collectionTypeLatest, offset, limit))\n      }\n\n  }\n\n  \"createSharedCollection\" should {\n\n    \"return a valid response if the services return a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.createCollection(any, any)(any, any) returns\n          TaskService {\n            Task(\n              Either.right(\n                ServiceClientResponse(\n                  statusCode,\n                  Some(CreateCollectionResponse(sharedCollectionId, packageStats)))))\n          }\n\n        val result = apiServices\n          .createSharedCollection(\n            sharedCollectionName,\n            author,\n            apiPackages,\n            categoryStr,\n            sharedCollectionPackageIcon,\n            community)\n          .value\n          .run\n        result shouldEqual Right(sharedCollectionId)\n\n        there was one(apiService)\n          .createCollection(===(createCollectionRequest), ===(serviceHeader))(any, any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        val result = apiServices\n          .createSharedCollection(\n            sharedCollectionName,\n            author,\n            apiPackages,\n            categoryStr,\n            sharedCollectionPackageIcon,\n            community)\n          .value\n          .run\n        result must beAnInstanceOf[Left[ApiServiceConfigurationException, _]]\n      }\n\n    \"return an ApiServiceException when the service return None\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.createCollection(any, any)(any, any) returns\n          TaskService(Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        val result = apiServices\n          .createSharedCollection(\n            sharedCollectionName,\n            author,\n            apiPackages,\n            categoryStr,\n            sharedCollectionPackageIcon,\n            community)\n          .value\n          .run\n        result must beAnInstanceOf[Left[ApiServiceException, _]]\n      }\n\n    \"return an ApiServiceException when the service return an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.createCollection(any, any)(any, any) returns TaskService(\n          Task(Either.left(exception)))\n\n        val result = apiServices\n          .createSharedCollection(\n            sharedCollectionName,\n            author,\n            apiPackages,\n            categoryStr,\n            sharedCollectionPackageIcon,\n            community)\n          .value\n          .run\n        result must beAnInstanceOf[Left[ApiServiceException, _]]\n      }\n\n  }\n\n  \"updateSharedCollection\" should {\n\n    \"return a valid response if the services return a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.updateCollection(any, any, any)(any, any) returns\n          TaskService(\n            Task(Either.right(ServiceClientResponse(statusCode, Some(updateCollectionResponse)))))\n\n        val result = apiServices\n          .updateSharedCollection(sharedCollectionId, Some(sharedCollectionName), apiPackages)\n          .value\n          .run\n        result shouldEqual Right(sharedCollectionId)\n\n        there was one(apiService).updateCollection(\n          ===(sharedCollectionId),\n          ===(updateCollectionRequest),\n          ===(serviceHeader))(any, any)\n      }\n\n    \"return a valid response if the services return a valid response but don't send a update info when the name is not set\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.updateCollection(any, any, any)(any, any) returns\n          TaskService(\n            Task(Either.right(ServiceClientResponse(statusCode, Some(updateCollectionResponse)))))\n\n        val result =\n          apiServices.updateSharedCollection(sharedCollectionId, None, apiPackages).value.run\n        result shouldEqual Right(sharedCollectionId)\n\n        there was one(apiService).updateCollection(\n          ===(sharedCollectionId),\n          ===(updateCollectionRequest.copy(collectionInfo = None)),\n          ===(serviceHeader))(any, any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException] {\n          apiServices\n            .updateSharedCollection(sharedCollectionId, Some(sharedCollectionName), apiPackages)\n        }\n      }\n\n    \"return an ApiServiceException when the service returns None\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.updateCollection(any, any, any)(any, any) returns\n          TaskService(Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        mustLeft[ApiServiceException] {\n          apiServices\n            .updateSharedCollection(sharedCollectionId, Some(sharedCollectionName), apiPackages)\n        }\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.updateCollection(any, any, any)(any, any) returns TaskService(\n          Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException] {\n          apiServices\n            .updateSharedCollection(sharedCollectionId, Some(sharedCollectionName), apiPackages)\n        }\n      }\n\n  }\n\n  \"getPublishedCollections\" should {\n\n    \"return a valid response if the services return a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.getCollections(any)(any) returns\n          TaskService {\n            Task(Either.right(ServiceClientResponse(statusCode, Some(collectionsResponse))))\n          }\n\n        val result = apiServices.getPublishedCollections().value.run\n        result must beLike {\n          case Right(shareCollections) =>\n            shareCollections.size shouldEqual seqCollection.size\n            shareCollections map (s =>\n                                    Option(s.sharedCollectionId)) shouldEqual (seqCollection map (_.sharedCollectionId))\n        }\n\n        there was one(apiService).getCollections(===(serviceMarketHeader))(any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](apiServices.getPublishedCollections())\n      }\n\n    \"return an ApiServiceException when the service returns None\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.getCollections(any)(any) returns TaskService(\n          Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        mustLeft[ApiServiceException](apiServices.getPublishedCollections())\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.getCollections(any)(any) returns TaskService(Task(Left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.getPublishedCollections())\n      }\n\n  }\n\n  \"getSubscriptions\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.getSubscriptions(any)(any) returns\n          TaskService {\n            Task(\n              Either.right(\n                ServiceClientResponse(\n                  statusCode,\n                  Some(SubscriptionsResponse(Seq(sharedCollectionId))))))\n          }\n\n        val result = apiServices.getSubscriptions().value.run\n        result shouldEqual Right(seqSubscription)\n\n        there was one(apiService).getSubscriptions(===(serviceHeader))(any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](apiServices.getSubscriptions())\n      }\n\n    \"return an ApiServiceException when the service returns None\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.getSubscriptions(any)(any) returns TaskService(\n          Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        mustLeft[ApiServiceException](apiServices.getSubscriptions())\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.getSubscriptions(any)(any) returns TaskService(Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.getSubscriptions())\n      }\n\n  }\n\n  \"subscribe\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.subscribe(any, any) returns\n          TaskService(Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        val result = apiServices.subscribe(sharedCollectionId).value.run\n\n        there was one(apiService).subscribe(sharedCollectionId, serviceHeader)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](apiServices.subscribe(sharedCollectionId))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.subscribe(any, any) returns TaskService(Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.subscribe(sharedCollectionId))\n      }\n\n  }\n\n  \"unsubscribe\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.unsubscribe(any, any) returns\n          TaskService(Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        val result = apiServices.unsubscribe(sharedCollectionId).value.run\n\n        there was one(apiService).unsubscribe(sharedCollectionId, serviceHeader)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](apiServices.unsubscribe(sharedCollectionId))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.unsubscribe(any, any) returns TaskService(Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.unsubscribe(sharedCollectionId))\n      }\n  }\n\n  \"updateViewShareCollection\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.updateViewShareCollection(any, any) returns\n          TaskService(Task(Either.right(ServiceClientResponse(statusCode, None))))\n\n        val result = apiServices.updateViewShareCollection(sharedCollectionId).value.run\n\n        there was one(apiService).updateViewShareCollection(sharedCollectionId, serviceHeader)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](\n          apiServices.updateViewShareCollection(sharedCollectionId))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.updateViewShareCollection(any, any) returns TaskService(\n          Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.updateViewShareCollection(sharedCollectionId))\n      }\n  }\n\n  \"rankApps\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.rankApps(any, any)(any, any) returns\n          TaskService {\n            Task(Either.right(ServiceClientResponse(statusCode, Some(rankAppsResponse))))\n          }\n\n        val result = apiServices.rankApps(seqPackagesByCategory, Some(location)).value.run\n        result must beLike {\n          case Right(response) =>\n            response.map(_.category.name) shouldEqual rankAppMap.keys\n            response.map(_.packages) shouldEqual rankAppMap.values\n        }\n\n        there was one(apiService).rankApps(===(rankAppsRequest), ===(serviceHeader))(any, any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](\n          apiServices.rankApps(seqPackagesByCategory, Some(location)))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.rankApps(any, any)(any, any) returns TaskService(Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](apiServices.rankApps(seqPackagesByCategory, Some(location)))\n      }\n\n  }\n\n  \"rankAppsByMoment\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.rankAppsByMoment(any, any)(any, any) returns\n          TaskService {\n            Task(Either.right(ServiceClientResponse(statusCode, Some(rankAppsByMomentResponse))))\n          }\n\n        val result = apiServices\n          .rankAppsByMoment(apiPackages, momentTypeSeq.take(3), Some(location), limit)\n          .value\n          .run\n        result must beLike {\n          case Right(response) =>\n            response.map(_.moment.name) shouldEqual momentTypeSeq.take(3)\n            response.map(_.packages) shouldEqual List(\n              List(apiPackages(0)),\n              List(apiPackages(1)),\n              List(apiPackages(2)))\n        }\n\n        there was one(apiService)\n          .rankAppsByMoment(===(rankAppsByMomentRequest), ===(serviceHeader))(any, any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](\n          apiServices.rankAppsByMoment(apiPackages, momentTypeSeq, Some(location), limit))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.rankAppsByMoment(any, any)(any, any) returns TaskService(\n          Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](\n          apiServices.rankAppsByMoment(apiPackages, momentTypeSeq, Some(location), limit))\n      }\n\n  }\n\n  \"rankWidgetsByMoment\" should {\n\n    \"return a valid response if the services returns a valid response\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.rankWidgetsByMoment(any, any)(any, any) returns\n          TaskService {\n            Task(\n              Either.right(ServiceClientResponse(statusCode, Some(rankWidgetsByMomentResponse))))\n          }\n\n        val result = apiServices\n          .rankWidgetsByMoment(apiPackages, momentTypeSeq.take(3), Some(location), limit)\n          .value\n          .run\n        result must beLike {\n          case Right(response) =>\n            response.map(_.moment.name) shouldEqual momentTypeSeq.take(3)\n            response.map(_.widgets.map(_.packageName)) shouldEqual List(\n              List(apiPackages(0)),\n              List(apiPackages(1)),\n              List(apiPackages(2)))\n        }\n\n        there was one(apiService)\n          .rankWidgetsByMoment(===(rankWidgetsByMomentRequest), ===(serviceHeader))(any, any)\n      }\n\n    \"return an ApiServiceConfigurationException when the base url is empty\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns \"\"\n\n        mustLeft[ApiServiceConfigurationException](\n          apiServices.rankWidgetsByMoment(apiPackages, momentTypeSeq, Some(location), limit))\n      }\n\n    \"return an ApiServiceException when the service returns an exception\" in\n      new ApiServicesScope {\n\n        apiService.baseUrl returns baseUrl\n        apiService.rankWidgetsByMoment(any, any)(any, any) returns TaskService(\n          Task(Either.left(exception)))\n\n        mustLeft[ApiServiceException](\n          apiServices.rankWidgetsByMoment(apiPackages, momentTypeSeq, Some(location), limit))\n      }\n\n  }\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/apps/impl/AppsServicesImplData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.apps.impl\n\nimport cards.nine.models.ApplicationData\nimport cards.nine.models.types.{Communication, Misc}\n\nimport scala.util.Random\n\ntrait AppsServicesImplData {\n\n  val androidFeedback = \"com.google.android.feedback\"\n\n  val name: String                     = Random.nextString(5)\n  val packageName: String              = Random.nextString(5)\n  val className: String                = Random.nextString(5)\n  val resourceIcon: Int                = Random.nextInt(10)\n  val dateInstalled: Long              = Random.nextLong()\n  val dateUpdate: Long                 = Random.nextLong()\n  val version: String                  = Random.nextInt(10).toString\n  val installedFromGooglePlay: Boolean = true\n\n  val applicationList = createSeqApplication()\n\n  val defaultApplicationList = createSeqApplication(10)\n\n  val sampleApp1 = applicationList(0)\n\n  val sampleApp2 = applicationList(1)\n\n  val validPackageName = sampleApp1.packageName\n\n  val invalidPackageName = \"cards.nine.test.sampleapp3\"\n\n  def createSeqApplication(\n      num: Int = 2,\n      name: String = name,\n      packageName: String = packageName,\n      className: String = className,\n      resourceIcon: Int = resourceIcon,\n      dateInstalled: Long = dateInstalled,\n      dateUpdate: Long = dateUpdate,\n      version: String = version,\n      installedFromGooglePlay: Boolean = installedFromGooglePlay): Seq[ApplicationData] =\n    List.tabulate(num)(\n      item =>\n        ApplicationData(\n          name = name,\n          packageName = packageName,\n          className = className,\n          category = Misc,\n          dateInstalled = dateInstalled,\n          dateUpdated = dateUpdate,\n          version = version,\n          installedFromGooglePlay = installedFromGooglePlay))\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/apps/impl/AppsServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.apps.impl\n\nimport android.content.Intent\nimport android.content.pm._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.models.ApplicationData\nimport cards.nine.services.apps.AppsInstalledException\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\nimport scala.collection.JavaConversions._\n\ntrait AppsServicesImplSpecification extends Specification with Mockito {\n\n  trait AppsServicesImplScope extends Scope with AppsServicesImplData {\n\n    val packageManager = mock[PackageManager]\n    val contextSupport = mock[ContextSupport]\n    contextSupport.getPackageManager returns packageManager\n\n    val mockIntent = mock[Intent]\n\n    def createMockResolveInfo(sampleApp: ApplicationData): ResolveInfo = {\n      val sampleResolveInfo   = mock[ResolveInfo]\n      val mockActivityInfo    = mock[ActivityInfo]\n      val mockApplicationInfo = mock[ApplicationInfo]\n      sampleResolveInfo.activityInfo = mockActivityInfo\n      mockActivityInfo.name = sampleApp.className\n      mockActivityInfo.applicationInfo = mockApplicationInfo\n      mockApplicationInfo.packageName = sampleApp.packageName\n      sampleResolveInfo.loadLabel(packageManager) returns sampleApp.name\n      sampleResolveInfo\n    }\n\n    def createMockPackageInfo(sampleApp: ApplicationData): PackageInfo = {\n      val samplePackageInfo   = mock[PackageInfo]\n      val mockApplicationInfo = mock[ApplicationInfo]\n      samplePackageInfo.applicationInfo = mockApplicationInfo\n      samplePackageInfo.packageName = sampleApp.packageName\n      mockApplicationInfo.name = sampleApp.name\n      mockApplicationInfo.className = sampleApp.className\n      samplePackageInfo.firstInstallTime = sampleApp.dateInstalled\n      samplePackageInfo.lastUpdateTime = sampleApp.dateUpdated\n      samplePackageInfo.versionCode = sampleApp.version.toInt\n      samplePackageInfo\n    }\n\n    val mockApps = List(createMockResolveInfo(sampleApp1), createMockResolveInfo(sampleApp2))\n\n    val packageInfo1 = createMockPackageInfo(sampleApp1)\n    val packageInfo2 = createMockPackageInfo(sampleApp2)\n\n    packageManager.getPackageInfo(sampleApp1.packageName, 0) returns packageInfo1\n    packageManager.getPackageInfo(sampleApp2.packageName, 0) returns packageInfo2\n    packageManager.getInstallerPackageName(sampleApp1.packageName) returns androidFeedback\n    packageManager.getInstallerPackageName(sampleApp2.packageName) returns androidFeedback\n\n    val mockAppsServicesImpl = new AppsServicesImpl\n\n    val exception = AppsInstalledException(\"\")\n\n  }\n\n}\n\nclass AppsServicesImplSpec extends AppsServicesImplSpecification {\n\n  \"Apps Services\" should {\n\n    \"getInstalledApplications\" should {\n\n      \"returns the list of installed apps when they exist\" in\n        new AppsServicesImplScope {\n\n          packageManager.queryIntentActivities(any, any) returns mockApps\n          val result = mockAppsServicesImpl.getInstalledApplications(contextSupport).value.run\n          result shouldEqual Right(applicationList)\n        }\n\n      \"returns an AppsInstalledException when no apps exist\" in\n        new AppsServicesImplScope {\n\n          packageManager.queryIntentActivities(any, any) throws exception\n          val result = mockAppsServicesImpl.getInstalledApplications(contextSupport).value.run\n          result must beAnInstanceOf[Left[AppsInstalledException, _]]\n        }\n    }\n\n    \"getApplication\" should {\n\n      \"returns the installed app when a valid packageName is provided\" in\n        new AppsServicesImplScope {\n\n          packageManager.resolveActivity(mockIntent, 0) returns mockApps.head\n          packageManager.getLaunchIntentForPackage(sampleApp1.packageName) returns mockIntent\n\n          val result =\n            mockAppsServicesImpl.getApplication(validPackageName)(contextSupport).value.run\n          result shouldEqual Right(sampleApp1)\n        }\n\n      \"returns an AppsInstalledException when an invalid packageName is provided\" in\n        new AppsServicesImplScope {\n\n          packageManager.getLaunchIntentForPackage(invalidPackageName) throws exception\n          val result =\n            mockAppsServicesImpl.getApplication(invalidPackageName)(contextSupport).value.run\n          result must beAnInstanceOf[Left[AppsInstalledException, _]]\n        }\n\n      \"returns an AppsInstalledException when the resolveActivity method fails\" in\n        new AppsServicesImplScope {\n\n          packageManager.resolveActivity(mockIntent, 0) throws exception\n          val result =\n            mockAppsServicesImpl.getApplication(validPackageName)(contextSupport).value.run\n          result must beAnInstanceOf[Left[AppsInstalledException, _]]\n        }\n\n      \"returns an AppsInstalledException when the getPackageInfo method fails\" in\n        new AppsServicesImplScope {\n\n          packageManager.getPackageInfo(sampleApp1.packageName, 0) throws exception\n          val result =\n            mockAppsServicesImpl.getApplication(validPackageName)(contextSupport).value.run\n          result must beAnInstanceOf[Left[AppsInstalledException, _]]\n        }\n    }\n\n    \"getDefaultApps\" should {\n\n      \"returns the list of installed default apps when they exist\" in\n        new AppsServicesImplScope {\n\n          packageManager.queryIntentActivities(any, any) returns mockApps\n          val result = mockAppsServicesImpl.getDefaultApps(contextSupport).value.run\n          result shouldEqual Right(defaultApplicationList)\n        }\n\n      \"returns an AppsInstalledException when no default apps exist\" in\n        new AppsServicesImplScope {\n\n          packageManager.queryIntentActivities(any, any) throws exception\n          val result = mockAppsServicesImpl.getDefaultApps(contextSupport).value.run\n          result must beAnInstanceOf[Left[AppsInstalledException, _]]\n        }\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/calls/impl/CallsServicesImplData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.calls.impl\n\nimport cards.nine.models.Call\nimport cards.nine.models.types._\n\ntrait CallsServicesImplData {\n\n  val phoneHome   = \"666666666\"\n  val phoneWork   = \"777777777\"\n  val phoneMobile = \"888888888\"\n  val phoneOther  = \"999999999\"\n\n  val seqPhones        = Seq(phoneHome, phoneWork, phoneMobile, phoneOther)\n  val seqPhoneCategory = Seq(PhoneHome, PhoneWork, PhoneMobile, PhoneOther)\n  val seqCallType      = Seq(IncomingType, OutgoingType, MissedType, OtherType)\n\n  val calls = generateCalls\n\n  def generateCalls: Seq[Call] =\n    0 to 3 map { i =>\n      Call(seqPhones(i), Option(s\"contact$i\"), seqPhoneCategory(i), 1L, seqCallType(i))\n    }\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/calls/impl/CallsServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.calls.impl\n\nimport cards.nine.commons.contentresolver.ContentResolverWrapperImpl\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.models.Call\nimport cards.nine.services.calls.{CallsServicesException, CallsServicesPermissionException}\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait CallsServicesSpecification extends Specification with Mockito with CallsServicesImplData {\n\n  trait CallsServicesScope extends Scope {\n\n    lazy val contentResolverWrapper = mock[ContentResolverWrapperImpl]\n\n    lazy val callServices = new CallsServicesImpl(contentResolverWrapper)\n\n  }\n}\n\nclass CallsServicesImplSpec extends CallsServicesSpecification {\n\n  \"CallsService component\" should {\n\n    \"getCalls\" should {\n\n      \"returns all the contacts from the content resolver\" in\n        new CallsServicesScope {\n\n          contentResolverWrapper.fetchAll[Call](any, any, any, any, any)(any) returns calls\n          val result = callServices.getLastCalls.value.run\n          result shouldEqual Right(calls)\n        }\n\n      \"return a CallsServicePermissionException when the content resolver throws a SecurityException\" in\n        new CallsServicesScope {\n\n          val contentResolverException = new SecurityException(\"Irrelevant message\")\n          contentResolverWrapper.fetchAll[Call](any, any, any, any, any)(any) throws contentResolverException\n          val result = callServices.getLastCalls.value.run\n          result must beAnInstanceOf[Left[CallsServicesPermissionException, _]]\n        }\n\n      \"return a CallsServicesException when the content resolver throws an exception\" in\n        new CallsServicesScope {\n\n          val contentResolverException = new RuntimeException(\"Irrelevant message\")\n          contentResolverWrapper.fetchAll[Call](any, any, any, any, any)(any) throws contentResolverException\n          val result = callServices.getLastCalls.value.run\n          result must beAnInstanceOf[Left[CallsServicesException, _]]\n        }\n    }\n\n  }\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/connectivity/impl/ConnectivityServicesImplData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.connectivity.impl\n\nimport android.net.wifi.WifiConfiguration\n\ntrait ConnectivityServicesImplData {\n\n  val ssidResult = \"My Wifi\"\n\n  val ssid: String = \"\\\"\" + ssidResult + \"\\\"\"\n\n  val ssidWithQuotesResult = \"My wifi with \\\"quotes\\\"\"\n\n  val ssidWithQuotes: String = \"\\\"\" + ssidWithQuotesResult + \"\\\"\"\n\n  val ssidWithoutQuotes: String = ssidResult\n\n  val ssidWithError: String = \"\\\"\\\"\"\n\n  val networksUnsorted = Seq(\"znf\", \"Abc\", \"47 deg\", \"trn\", \"bcb\", \"BB\", \"ant\")\n\n  val wifiConfigurations = networksUnsorted map { network =>\n    val wc = new WifiConfiguration\n    wc.SSID = network\n    wc\n  }\n\n  val networksSorted = Seq(\"47 deg\", \"Abc\", \"ant\", \"BB\", \"bcb\", \"trn\", \"znf\")\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/connectivity/impl/ConnectivityServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.connectivity.impl\n\nimport java.util\n\nimport android.bluetooth.BluetoothDevice\nimport android.content.Context\nimport android.net.wifi.{WifiConfiguration, WifiInfo, WifiManager}\nimport android.net.{ConnectivityManager, NetworkInfo}\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.javaNull\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\nimport scala.collection.JavaConversions._\n\ntrait ConnectivityImplSpecification\n    extends TaskServiceSpecification\n    with Mockito\n    with ConnectivityServicesImplData {\n\n  trait ConnectivityImplScope extends Scope {\n\n    val bluetoothDevice: util.Set[BluetoothDevice] = new java.util.TreeSet[BluetoothDevice]()\n\n    val mockContextSupport = mock[ContextSupport]\n    mockContextSupport.getBluetoothDevicesConnected returns Set.empty\n\n    val mockContext = mock[Context]\n    mockContextSupport.context returns mockContext\n\n    val mockConnectivityManager = mock[ConnectivityManager]\n    val mockNetWorkInfo         = mock[NetworkInfo]\n    val mockWifiManager         = mock[WifiManager]\n    val mockWifiInfo            = mock[WifiInfo]\n\n    val connectivityServicesImpl = new ConnectivityServicesImpl {\n      override protected def getBondedDevices: util.Set[BluetoothDevice] = bluetoothDevice\n    }\n  }\n\n}\n\nclass ConnectivityServicesImplSpec extends ConnectivityImplSpecification {\n\n  \"getCurrentSSID\" should {\n    \"returns the current SSID\" in\n      new ConnectivityImplScope {\n\n        mockWifiManager.getConfiguredNetworks returns wifiConfigurations\n        mockNetWorkInfo.getType returns ConnectivityManager.TYPE_WIFI\n        mockContext.getSystemService(Context.CONNECTIVITY_SERVICE) returns mockConnectivityManager\n        mockConnectivityManager.getActiveNetworkInfo returns mockNetWorkInfo\n        mockNetWorkInfo.isConnected returns true\n        mockNetWorkInfo.getExtraInfo returns ssid\n\n        connectivityServicesImpl\n          .getCurrentSSID(mockContextSupport)\n          .mustRight(_ shouldEqual Some(ssidResult))\n\n      }\n\n    \"returns the current SSID with quotes\" in\n      new ConnectivityImplScope {\n\n        mockWifiManager.getConfiguredNetworks returns wifiConfigurations\n        mockNetWorkInfo.getType returns ConnectivityManager.TYPE_WIFI\n        mockContext.getSystemService(Context.CONNECTIVITY_SERVICE) returns mockConnectivityManager\n        mockConnectivityManager.getActiveNetworkInfo returns mockNetWorkInfo\n        mockNetWorkInfo.isConnected returns true\n        mockNetWorkInfo.getExtraInfo returns ssidWithQuotes\n\n        connectivityServicesImpl\n          .getCurrentSSID(mockContextSupport)\n          .mustRight(_ shouldEqual Some(ssidWithQuotesResult))\n      }\n\n    \"returns the current SSID without quotes\" in\n      new ConnectivityImplScope {\n\n        mockWifiManager.getConfiguredNetworks returns wifiConfigurations\n        mockNetWorkInfo.getType returns ConnectivityManager.TYPE_WIFI\n        mockContext.getSystemService(Context.CONNECTIVITY_SERVICE) returns mockConnectivityManager\n        mockConnectivityManager.getActiveNetworkInfo returns mockNetWorkInfo\n        mockNetWorkInfo.isConnected returns true\n        mockNetWorkInfo.getExtraInfo returns ssidWithoutQuotes\n\n        connectivityServicesImpl\n          .getCurrentSSID(mockContextSupport)\n          .mustRight(_ shouldEqual Some(ssidWithoutQuotes))\n      }\n\n    \"returns None if SSID is empty \" in\n      new ConnectivityImplScope {\n\n        mockWifiManager.getConfiguredNetworks returns wifiConfigurations\n        mockNetWorkInfo.getType returns ConnectivityManager.TYPE_WIFI\n        mockContext.getSystemService(Context.CONNECTIVITY_SERVICE) returns mockConnectivityManager\n        mockConnectivityManager.getActiveNetworkInfo returns mockNetWorkInfo\n        mockNetWorkInfo.isConnected returns true\n        mockNetWorkInfo.getExtraInfo returns ssidWithError\n\n        connectivityServicesImpl.getCurrentSSID(mockContextSupport).mustRightNone\n      }\n\n    \"returns None if there isn't connectivity manager\" in\n      new ConnectivityImplScope {\n\n        mockContext.getSystemService(Context.CONNECTIVITY_SERVICE) returns javaNull\n        connectivityServicesImpl.getCurrentSSID(mockContextSupport).mustRightNone\n      }\n\n    \"returns None if there isn't active network\" in\n      new ConnectivityImplScope {\n\n        mockContext.getSystemService(Context.CONNECTIVITY_SERVICE) returns mockConnectivityManager\n        mockConnectivityManager.getActiveNetworkInfo returns javaNull\n        connectivityServicesImpl.getCurrentSSID(mockContextSupport).mustRightNone\n      }\n\n    \"returns None if it is not connected\" in\n      new ConnectivityImplScope {\n\n        mockContext.getSystemService(Context.CONNECTIVITY_SERVICE) returns mockConnectivityManager\n        mockConnectivityManager.getActiveNetworkInfo returns mockNetWorkInfo\n        mockNetWorkInfo.isConnected returns false\n        connectivityServicesImpl.getCurrentSSID(mockContextSupport).mustRightNone\n      }\n\n    \"returns None if type isn't WIFI\" in\n      new ConnectivityImplScope {\n\n        mockContext.getSystemService(Context.CONNECTIVITY_SERVICE) returns mockConnectivityManager\n        mockConnectivityManager.getActiveNetworkInfo returns mockNetWorkInfo\n        mockNetWorkInfo.isConnected returns true\n        mockNetWorkInfo.getType returns ConnectivityManager.TYPE_MOBILE\n        connectivityServicesImpl.getCurrentSSID(mockContextSupport).mustRightNone\n      }\n  }\n\n  \"getConfiguredNetworks\" should {\n\n    \"returns list of networks sorted\" in\n      new ConnectivityImplScope {\n\n        mockContext.getSystemService(Context.WIFI_SERVICE) returns mockWifiManager\n        mockWifiManager.getConfiguredNetworks returns wifiConfigurations\n\n        connectivityServicesImpl\n          .getConfiguredNetworks(mockContextSupport)\n          .mustRight(_ shouldEqual networksSorted)\n      }\n\n    \"returns empty list if android don't return data\" in\n      new ConnectivityImplScope {\n\n        mockWifiManager.getConfiguredNetworks returns Seq.empty[WifiConfiguration]\n        connectivityServicesImpl\n          .getConfiguredNetworks(mockContextSupport)\n          .mustRight(_ shouldEqual Seq.empty)\n      }\n\n    \"returns empty list if android returns null\" in\n      new ConnectivityImplScope {\n\n        mockWifiManager.getConfiguredNetworks returns javaNull\n        connectivityServicesImpl\n          .getConfiguredNetworks(mockContextSupport)\n          .mustRight(_ shouldEqual Seq.empty)\n      }\n\n  }\n\n  \"getPairedDevices\" should {\n\n    \"returns empty list of devices if doesn't found paired bluetooth\" in\n      new ConnectivityImplScope {\n        connectivityServicesImpl.getPairedDevices.mustRight(_ shouldEqual Seq.empty)\n      }\n\n  }\n\n  \"getBluetoothConnected\" should {\n\n    \"returns empty list of devices if there aren't devices connected\" in\n      new ConnectivityImplScope {\n        connectivityServicesImpl\n          .getBluetoothConnected(mockContextSupport)\n          .mustRight(_ shouldEqual Set.empty)\n\n        there was one(mockContextSupport).getBluetoothDevicesConnected\n      }\n\n    \"returns list of devices if there are devices connected\" in\n      new ConnectivityImplScope {\n        val devices = Set(\"My Bluetooth 1\", \"My Bluetooth 2\")\n\n        mockContextSupport.getBluetoothDevicesConnected returns devices\n\n        connectivityServicesImpl\n          .getBluetoothConnected(mockContextSupport)\n          .mustRight(_ shouldEqual devices)\n\n        there was one(mockContextSupport).getBluetoothDevicesConnected\n      }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/contacts/impl/ContactsServicesImplData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.contacts.impl\n\nimport cards.nine.commons.test.repository.{IntDataType, MockCursor, StringDataType}\nimport cards.nine.models._\nimport cards.nine.models.types._\nimport cards.nine.services.contacts.Fields\n\ntrait ContactsServicesImplData {\n\n  val firstLookupKey = \"lookupKey 1\"\n\n  val nonExistentLookupKey = \"nonExistentLookupKey\"\n\n  val emailHome  = \"sample_home@domain.com\"\n  val emailWork  = \"sample_work@domain.com\"\n  val emailOther = \"sample_other@domain.com\"\n\n  val nonExistentEmail = \"not_found@domain.com\"\n\n  val phoneHome   = \"666666666\"\n  val phoneWork   = \"777777777\"\n  val phoneMobile = \"888888888\"\n  val phoneOther  = \"999999999\"\n\n  val nonExistentPhone = \"000000000\"\n\n  val contacts = generateContacts(num = 10, withEmails = false, withPhones = false)\n\n  val contact =\n    generateContacts(1, withEmails = true, withPhones = true).head.copy(lookupKey = firstLookupKey)\n\n  val contactInfo = contact.info\n\n  val contactLookupKeyAndEmails =\n    contactInfo.map(_.emails.map(email => (contact.lookupKey, email))).toSeq.flatten\n\n  val contactLookupKeyAndPhones =\n    contactInfo.map(_.phones.map(phone => (contact.lookupKey, phone))).toSeq.flatten\n\n  val contactWithEmail = generateContacts(1, withEmails = true, withPhones = false).headOption\n\n  val contactWithPhone = generateContacts(1, withEmails = false, withPhones = true).headOption\n\n  val testMockKeyword = \"mock-keyword\"\n\n  def generateContacts(\n      num: Int,\n      withEmails: Boolean = true,\n      withPhones: Boolean = true): Seq[Contact] =\n    1 to num map { i =>\n      Contact(\n        s\"name $i\",\n        s\"lookupKey $i\",\n        s\"content://photoUri/$i\",\n        i % 2 == 0,\n        i % 5 == 0,\n        generateContactInfo(withEmails, withPhones))\n    }\n\n  def generateContactInfo(withEmails: Boolean, withPhones: Boolean): Option[ContactInfo] =\n    (withPhones, withEmails) match {\n      case (false, false) =>\n        None\n      case _ =>\n        Some(\n          ContactInfo(\n            if (withEmails) generateEmails else Seq.empty,\n            if (withPhones) generatePhones else Seq.empty))\n    }\n\n  def generateEmails: Seq[ContactEmail] =\n    Seq(\n      ContactEmail(emailHome, EmailHome),\n      ContactEmail(emailWork, EmailWork),\n      ContactEmail(emailOther, EmailOther))\n\n  def generatePhones: Seq[ContactPhone] =\n    Seq(\n      ContactPhone(phoneHome, PhoneHome),\n      ContactPhone(phoneWork, PhoneWork),\n      ContactPhone(phoneMobile, PhoneMobile),\n      ContactPhone(phoneOther, PhoneOther))\n\n  trait AlphabeticalMockCursor extends MockCursor {\n\n    val contactsIterator: Seq[String] =\n      Seq(\"!aaa\", \"2bbb\", \"?ccc\", \"1ddd\", \"#eeee\", \"Abc\", \"Acd\", \"Ade\", \"Bcd\", \"Bde\", \"Bef\", \"Cde\")\n\n    val data = Seq((Fields.DISPLAY_NAME, 0, contactsIterator, StringDataType))\n\n    prepareCursor[String](contactsIterator.size, data)\n  }\n\n  trait ContactsMockCursor extends MockCursor {\n\n    val cursorData = Seq(\n      (\"display_name\", 0, contacts map (_.name), StringDataType),\n      (\"lookup\", 1, contacts map (_.lookupKey), StringDataType),\n      (\"has_phone_number\", 3, contacts map (c => booleanToInt(c.hasPhone)), IntDataType),\n      (\"starred\", 4, contacts map (c => booleanToInt(c.favorite)), IntDataType)\n    )\n\n    prepareCursor[Contact](contacts.size, cursorData)\n\n  }\n\n  trait EmptyPhoneMockCursor extends MockCursor {\n\n    val cursorPhone = Seq(\n      (\"number\", 0, Seq.empty, StringDataType),\n      (\"category\", 1, Seq.empty, StringDataType)\n    )\n    prepareCursor[ContactPhone](0, cursorPhone)\n  }\n\n  trait EmptyEmailMockCursor extends MockCursor {\n\n    val cursorEmail = Seq(\n      (\"address\", 0, Seq.empty, StringDataType),\n      (\"category\", 1, Seq.empty, StringDataType)\n    )\n    prepareCursor[ContactEmail](0, cursorEmail)\n  }\n\n  val contactCounters = Seq(\n    TermCounter(\"#\", 5),\n    TermCounter(\"A\", 3),\n    TermCounter(\"B\", 3),\n    TermCounter(\"C\", 1)\n  )\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/contacts/impl/ContactsServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.contacts.impl\n\nimport android.net.Uri\nimport cards.nine.commons.contentresolver.Conversions._\nimport cards.nine.commons.contentresolver.{ContentResolverWrapperImpl, UriCreator}\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.models.IterableCursor._\nimport cards.nine.models.{Contact, ContactEmail, ContactPhone, IterableCursor}\nimport cards.nine.services.contacts.ContactsContentProvider._\nimport cards.nine.services.contacts.{\n  ContactNotFoundException,\n  ContactsServiceException,\n  ContactsServicePermissionException,\n  Fields\n}\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait ContactsServicesSpecification\n    extends TaskServiceSpecification\n    with Mockito\n    with ContactsServicesImplData {\n\n  val contentResolverException = new RuntimeException(\"Irrelevant message\")\n\n  val securityException = new SecurityException(\"Irrelevant message\")\n\n  trait ContactsServicesScope extends Scope {\n\n    lazy val contentResolverWrapper = mock[ContentResolverWrapperImpl]\n\n    lazy val uriCreator = mock[UriCreator]\n\n    lazy val mockUri = mock[Uri]\n\n    lazy val nonExistentMockUri = mock[Uri]\n\n    uriCreator.withAppendedPath(Fields.PHONE_LOOKUP_URI, phoneHome) returns mockUri\n\n    uriCreator\n      .withAppendedPath(Fields.PHONE_LOOKUP_URI, nonExistentPhone) returns nonExistentMockUri\n\n    lazy val contactsServices = new ContactsServicesImpl(contentResolverWrapper, uriCreator)\n\n  }\n\n}\n\nclass ContactsServicesImplSpec extends ContactsServicesSpecification {\n\n  \"ContactsService component\" should {\n\n    \"getContacts\" should {\n\n      \"returns all the contacts from the content resolver\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetchAll[Contact](any, any, any, any, any)(any) returns contacts\n          val result = contactsServices.getContacts.run\n          result shouldEqual Right(contacts)\n        }\n\n      \"return a ContactsServiceException when the content resolver throws an exception\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetchAll(any, any, any, any, any)(any) throws contentResolverException\n          val result = contactsServices.getContacts.run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n    }\n\n    \"getAlphabeticalCounterContacts\" should {\n\n      \"return a sequence of ContactCounter sort alphabetically\" in\n        new ContactsServicesScope with AlphabeticalMockCursor {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n\n          val result = contactsServices.getAlphabeticalCounterContacts.run\n          result shouldEqual Right(contactCounters)\n        }\n\n      \"return a ContactsServiceException when the content resolver throws an exception\" in\n        new ContactsServicesScope {\n\n          lazy val contactsServicesException =\n            new ContactsServicesImpl(contentResolverWrapper, uriCreator) {\n              override protected def getNamesAlphabetically: Seq[String] =\n                throw contentResolverException\n            }\n          val result = contactsServicesException.getAlphabeticalCounterContacts.run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n    }\n\n    \"getIterableContacts\" should {\n\n      \"return an IterableCursor of contacts by keyword sort by name\" in\n        new ContactsServicesScope with ContactsMockCursor {\n\n          contentResolverWrapper.fetchAll[ContactEmail](any, any, any, any, any)(any) returns Seq.empty\n          contentResolverWrapper.fetchAll[ContactPhone](any, any, any, any, any)(any) returns Seq.empty\n          contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n          contacts.foreach { c =>\n            val uri = mock[Uri]\n            uriCreator.withAppendedPath(any, ===(c.lookupKey)) returns uri\n            uri.toString returns c.photoUri\n          }\n          val result = contactsServices.getIterableContacts.run\n\n          result must beLike {\n            case Right(iterator) =>\n              toSeq(iterator) shouldEqual contacts\n          }\n        }\n      \"return an a RepositoryException when a exception is thrown \" in\n        new AlphabeticalMockCursor with ContactsServicesScope {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) throws contentResolverException\n          val result = contactsServices.getIterableContacts.run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n    }\n\n    \"getIterableContactsByKeyword\" should {\n\n      \"return an IterableCursor of Contacts\" in\n        new ContactsServicesScope with ContactsMockCursor {\n\n          contentResolverWrapper.fetchAll[ContactEmail](any, any, any, any, any)(any) returns Seq.empty\n          contentResolverWrapper.fetchAll[ContactPhone](any, any, any, any, any)(any) returns Seq.empty\n          contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n          contacts.foreach { c =>\n            val uri = mock[Uri]\n            uriCreator.withAppendedPath(any, ===(c.lookupKey)) returns uri\n            uri.toString returns c.photoUri\n          }\n          val result = contactsServices.getIterableContactsByKeyword(keyword = testMockKeyword).run\n\n          result must beLike {\n            case Right(iterator) =>\n              toSeq(iterator) shouldEqual contacts\n          }\n        }\n      \"return an a RepositoryException when a exception is thrown \" in\n        new AlphabeticalMockCursor with ContactsServicesScope {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) throws contentResolverException\n          val result = contactsServices.getIterableContactsByKeyword(keyword = testMockKeyword).run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n    }\n\n    \"fetchContactByEmail\" should {\n\n      \"return the contact from the content resolver for an existent email\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetch[Contact](any, any, any, any, any)(any) returns contactWithEmail\n          val result = contactsServices.fetchContactByEmail(emailHome).run\n          result shouldEqual Right(contactWithEmail)\n        }\n\n      \"return None from the content resolver for a non existent email\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetch(any, any, any, any, any)(any) returns None\n          val result = contactsServices.fetchContactByEmail(nonExistentEmail).run\n          result shouldEqual Right(None)\n        }\n\n      \"return a ContactsServiceException when the content resolver throws an exception\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetch(any, any, any, any, any)(any) throws contentResolverException\n          val result = contactsServices.fetchContactByEmail(emailHome).run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n\n    }\n\n    \"fetchContactByPhoneNumber\" should {\n\n      \"return the contact from the content resolver for an existent phone\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetch[Contact](any, any, any, any, any)(any) returns contactWithPhone\n          val result = contactsServices.fetchContactByPhoneNumber(phoneHome).run\n          result shouldEqual Right(contactWithPhone)\n        }\n\n      \"return None from the content resolver for a non existent email\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetch(any, any, any, any, any)(any) returns None\n          val result = contactsServices.fetchContactByPhoneNumber(nonExistentPhone).run\n          result shouldEqual Right(None)\n        }\n\n      \"return a ContactsServiceException when the content resolver throws an exception\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetch(any, any, any, any, any)(any) throws contentResolverException\n          val result = contactsServices.fetchContactByPhoneNumber(phoneHome).run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n    }\n\n    \"findContactByLookupKey\" should {\n\n      \"return the contact from the content resolver for an existent lookup key\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = Fields.CONTENT_URI,\n            projection = allFields,\n            where = Fields.LOOKUP_SELECTION,\n            whereParams = Seq(firstLookupKey))(\n            getListFromCursor(contactFromCursor(uriCreator = uriCreator, _))) returns Seq(contact)\n\n          contentResolverWrapper.fetchAll(\n            uri = Fields.EMAIL_CONTENT_URI,\n            projection = allEmailFields,\n            where = s\"${Fields.EMAIL_CONTACT_SELECTION} ('$firstLookupKey')\")(\n            getListFromCursor(lookupKeyAndEmailFromCursor)) returns contactLookupKeyAndEmails\n\n          contentResolverWrapper.fetchAll(\n            uri = Fields.PHONE_CONTENT_URI,\n            projection = allPhoneFields,\n            where = s\"${Fields.PHONE_CONTACT_SELECTION} ('$firstLookupKey')\")(\n            getListFromCursor(lookupKeyAndPhoneFromCursor)) returns contactLookupKeyAndPhones\n\n          val result = contactsServices.findContactByLookupKey(firstLookupKey).run\n          result shouldEqual Right(contact)\n        }\n\n      \"return a ContactNotFoundException for a non existent lookup key\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetchAll(any, any, any, any, any)(any) returns Seq.empty\n          val result = contactsServices.findContactByLookupKey(nonExistentLookupKey).run\n          result must beAnInstanceOf[Left[ContactNotFoundException, _]]\n        }\n\n      \"return a ContactsServiceException when the content resolver throws an exception\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetchAll(any, any, any, any, any)(any) throws contentResolverException\n          val result = contactsServices.findContactByLookupKey(firstLookupKey).run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n\n    }\n\n    \"populateContactInfo\" should {\n\n      \"return the contact from the content resolver with the info field populated\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetchAll(\n            uri = Fields.EMAIL_CONTENT_URI,\n            projection = allEmailFields,\n            where = s\"${Fields.EMAIL_CONTACT_SELECTION} ('$firstLookupKey')\")(\n            getListFromCursor(lookupKeyAndEmailFromCursor)) returns contactLookupKeyAndEmails\n\n          contentResolverWrapper.fetchAll(\n            uri = Fields.PHONE_CONTENT_URI,\n            projection = allPhoneFields,\n            where = s\"${Fields.PHONE_CONTACT_SELECTION} ('$firstLookupKey')\")(\n            getListFromCursor(lookupKeyAndPhoneFromCursor)) returns contactLookupKeyAndPhones\n\n          val result = contactsServices.populateContactInfo(Seq(contact.copy(info = None))).run\n          result shouldEqual Right(Seq(contact))\n        }\n\n      \"return a ContactsServiceException when the content resolver throws an exception\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetchAll(any, any, any, any, any)(any) throws contentResolverException\n\n          val result = contactsServices.findContactByLookupKey(firstLookupKey).run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n\n    }\n\n    \"getFavoriteContacts\" should {\n\n      \"returns favorites contacts from the content resolver\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetchAll[Contact](any, any, any, any, any)(any) returns contacts\n          val result = contactsServices.getFavoriteContacts.run\n          result shouldEqual Right(contacts)\n        }\n\n      \"return a ContactsServiceException when the content resolver throws an exception\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetchAll(any, any, any, any, any)(any) throws contentResolverException\n          val result = contactsServices.getFavoriteContacts.run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n\n    }\n    \"getIterableFavoriteContacts\" should {\n\n      \"return iterable favorite contacts\" in\n        new ContactsServicesScope with ContactsMockCursor {\n\n          contentResolverWrapper.fetchAll[ContactEmail](any, any, any, any, any)(any) returns Seq.empty\n          contentResolverWrapper.fetchAll[ContactPhone](any, any, any, any, any)(any) returns Seq.empty\n          contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n          contacts.foreach { c =>\n            val uri = mock[Uri]\n            uriCreator.withAppendedPath(any, ===(c.lookupKey)) returns uri\n            uri.toString returns c.photoUri\n          }\n          val result = contactsServices.getIterableFavoriteContacts.run\n\n          result must beLike {\n            case Right(iterator) =>\n              toSeq(iterator) shouldEqual contacts\n          }\n        }\n      \"return an a RepositoryException when a exception is thrown \" in\n        new AlphabeticalMockCursor with ContactsServicesScope {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) throws contentResolverException\n          val result = contactsServices.getIterableFavoriteContacts.run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n    }\n\n    \"getContactsWithPhone\" should {\n\n      \"returns contacts with phone numbers from the content resolver\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetchAll[Contact](any, any, any, any, any)(any) returns contacts\n          val result = contactsServices.getContactsWithPhone.run\n          result shouldEqual Right(contacts)\n        }\n\n      \"return a ContactsServiceException when the content resolver throws an exception\" in\n        new ContactsServicesScope {\n\n          contentResolverWrapper.fetchAll(any, any, any, any, any)(any) throws contentResolverException\n          val result = contactsServices.getContactsWithPhone.run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n\n    }\n    \"getIterableContactsWithPhone\" should {\n\n      \"return iterable contacts with phone number\" in\n        new ContactsServicesScope with ContactsMockCursor {\n\n          contentResolverWrapper.fetchAll[ContactEmail](any, any, any, any, any)(any) returns Seq.empty\n          contentResolverWrapper.fetchAll[ContactPhone](any, any, any, any, any)(any) returns Seq.empty\n          contentResolverWrapper.getCursor(any, any, any, any, any) returns mockCursor\n          contacts.foreach { c =>\n            val uri = mock[Uri]\n            uriCreator.withAppendedPath(any, ===(c.lookupKey)) returns uri\n            uri.toString returns c.photoUri\n          }\n          val result = contactsServices.getIterableContactsWithPhone.run\n\n          result must beLike {\n            case Right(iterator) =>\n              toSeq(iterator) shouldEqual contacts\n          }\n        }\n      \"return an a RepositoryException when a exception is thrown \" in\n        new AlphabeticalMockCursor with ContactsServicesScope {\n\n          contentResolverWrapper.getCursor(any, any, any, any, any) throws contentResolverException\n          val result = contactsServices.getIterableContactsWithPhone.run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n    }\n\n    \"catchMapPermission\" should {\n\n      \"return a Right value when the function doesn't throw any exception\" in {\n        new ContactsServicesScope {\n          val value  = \"my-value\"\n          val result = contactsServices.catchMapPermission(value).run\n          result shouldEqual Right(value)\n        }\n      }\n\n      \"return a ContactsServicePermissionException when the function throws a SecurityException\" in {\n        new ContactsServicesScope {\n          val result = contactsServices.catchMapPermission(throw securityException).run\n          result must beAnInstanceOf[Left[ContactsServicePermissionException, _]]\n        }\n      }\n\n      \"return a ContactsServiceException when the function throws a RuntimeException\" in {\n        new ContactsServicesScope {\n          val result = contactsServices.catchMapPermission(throw contentResolverException).run\n          result must beAnInstanceOf[Left[ContactsServiceException, _]]\n        }\n      }\n\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/image/impl/ImageServicesImplData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.image.impl\n\nimport cards.nine.models._\n\ntrait ImageServicesImplData {\n\n  val fileFolder = \"/file/example\"\n\n  val packageName = \"com.fortysevendeg.ninecardslauncher.test\"\n\n  val className = \"ClassNameExample\"\n\n  val resultFileName = \"C\"\n\n  val resultFilePath = s\"$fileFolder/C\"\n\n  val resultFilePathPackage = s\"$fileFolder/$packageName\"\n\n  val uri = \"http://www.example.com/image.jpg\"\n\n  val name = \"Sample Name\"\n\n  val textToMeasure = \"M\"\n\n  val textSize = 71\n\n  val colorsList = List(1, 2, 3)\n\n  val densityDpi = 240\n\n  val widthPixels = 240\n\n  val heightPixels = 320\n\n  val bitmapName = \"aeiuo-12345\"\n\n  val resultFileSaveBitmap = s\"$fileFolder/$bitmapName\"\n\n  val bitmapPath = BitmapPath(name = \"\", path = resultFileSaveBitmap)\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/image/impl/ImageServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.image.impl\n\nimport java.io.File\n\nimport android.content.Intent.ShortcutIconResource\nimport android.content.pm.PackageManager\nimport android.content.res.Resources\nimport android.graphics.Bitmap\nimport android.util.DisplayMetrics\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.services.TaskService._\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.services.image._\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait ImageServicesImplSpecification extends Specification with Mockito {\n\n  val bitmapException = BitmapTransformationException(\"\")\n\n  val serviceFileException: TaskService[Unit] = TaskService(Task(Left(FileException(\"\"))))\n\n  trait ImageServicesScope extends Scope with ImageServicesImplData {\n\n    val contextSupport = mock[ContextSupport]\n    contextSupport.getPackageManager returns mock[PackageManager]\n    contextSupport.getAppIconsDir returns appIconDir\n    contextSupport.getResources returns resources\n\n    val appIconDir = mock[File]\n    appIconDir.getPath returns fileFolder\n\n    val resources = mock[Resources]\n    resources.getDisplayMetrics returns mock[DisplayMetrics]\n\n    val bitmap = mock[Bitmap]\n    val width  = Option(10)\n    val height = Option(10)\n\n    val saveBitmapTask = TaskService(Task {\n      Either.catchOnly[FileException] {\n        val file = mock[File]\n        file.exists() returns true\n        file.getAbsolutePath returns resultFileSaveBitmap\n        file\n      }\n    })\n\n    val mockTasks = mock[ImageServicesTasks]\n\n    val mockImageService = new ImageServicesImpl(mockTasks)\n\n    val mockShortcutIconResource = mock[ShortcutIconResource]\n\n  }\n\n}\n\nclass ImageServicesImplSpec extends ImageServicesImplSpecification {\n\n  \"Image Services.saveBitmaps\" should {\n\n    \"returns filename when the file exists\" in\n      new ImageServicesScope {\n\n        mockTasks.saveBitmap(any[File], any[Bitmap]) returns TaskService(\n          Task(Either.catchOnly[FileException](())))\n        mockTasks.getPathByName(any)(any) returns saveBitmapTask\n\n        val result = mockImageService.saveBitmap(bitmap, None, None)(contextSupport).value.run\n        result must beLike {\n          case Right(resultSaveBitmapPath) =>\n            resultSaveBitmapPath.path shouldEqual bitmapPath.path\n        }\n      }\n\n    \"returns filename when the file exists with resize\" in\n      new ImageServicesScope {\n\n        mockTasks.saveBitmap(any[File], any[Bitmap]) returns TaskService(\n          Task(Either.catchOnly[FileException](())))\n        mockTasks.getPathByName(any)(any) returns saveBitmapTask\n\n        val result = mockImageService.saveBitmap(bitmap, width, height)(contextSupport).value.run\n        result must beLike {\n          case Right(resultSaveBitmapPath) =>\n            resultSaveBitmapPath.path shouldEqual bitmapPath.path\n        }\n      }\n\n    \"returns a FileException if the bitmaps can't be stored\" in\n      new ImageServicesScope {\n\n        mockTasks.getPathByName(any)(any) returns saveBitmapTask\n        mockTasks.saveBitmap(any[File], any[Bitmap]) returns serviceFileException\n\n        val result = mockImageService.saveBitmap(bitmap, None, None)(contextSupport).value.run\n        result must beAnInstanceOf[Left[FileException, _]]\n      }\n  }\n\n  \"ImageServices.decodeShortcutIconResource\" should {\n\n    \"call to ImageServicesTasks.getBitmapFromShortcutIconResource\" in\n      new ImageServicesScope {\n\n        mockTasks.getBitmapFromShortcutIconResource(any)(any) returns TaskService.right(bitmap)\n\n        val result = mockImageService\n          .decodeShortcutIconResource(mockShortcutIconResource)(contextSupport)\n          .value\n          .run\n        result shouldEqual Right(bitmap)\n\n        there was one(mockTasks).getBitmapFromShortcutIconResource(===(mockShortcutIconResource))(\n          any)\n\n      }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/image/impl/ImageServicesTasksSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.image.impl\n\nimport java.io.{File, FileOutputStream, InputStream}\n\nimport android.content.Intent.ShortcutIconResource\nimport android.content.pm.PackageManager\nimport android.content.res.Resources\nimport android.graphics._\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.javaNull\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.services.image.{BitmapTransformationException, FileException}\nimport cards.nine.services.utils.ResourceUtils\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait ImageServicesTasksSpecification extends Specification with Mockito {\n\n  trait ImageServicesTasksScope extends Scope with ImageServicesImplData {\n\n    class ImageServicesTaskImpl extends ImageServicesTasks\n\n    val contextSupport = mock[ContextSupport]\n    val packageManager = mock[PackageManager]\n\n    val mockImageServicesTask = new ImageServicesTaskImpl\n\n    val mockResources = mock[Resources]\n    val mockFile      = mock[File]\n    val mockBitmap    = mock[Bitmap]\n\n    contextSupport.getPackageManager returns packageManager\n\n  }\n}\n\nclass ImageServicesTasksSpec extends ImageServicesTasksSpecification {\n\n  \"Image Services Tasks\" should {\n\n    \"return a File when a valid packageName and a valid className is provided\" in\n      new ImageServicesTasksScope {\n\n        contextSupport.getResources returns mockResources\n\n        val mockResourceUtils = new ResourceUtils {\n          override def getPath(filename: String)(implicit context: ContextSupport): String =\n            s\"$fileFolder/$filename\"\n        }\n\n        override val mockImageServicesTask = new ImageServicesTaskImpl {\n          override val resourceUtils = mockResourceUtils\n        }\n\n        val result = mockImageServicesTask.getPathByName(packageName)(contextSupport).value.run\n        result must beLike {\n          case Right(resultFile) =>\n            resultFile.getName shouldEqual packageName\n            resultFile.getPath shouldEqual resultFilePathPackage\n        }\n      }\n\n    \"return a FileException when getPath in resourceUtils returns an empty string\" in\n      new ImageServicesTasksScope {\n\n        val result = mockImageServicesTask.getPathByName(packageName)(contextSupport).value.run\n        result must beAnInstanceOf[Left[FileException, _]]\n\n      }\n\n    \"return a Bitmap when when a valid uri is provided\" in\n      new ImageServicesTasksScope {\n\n        val mockInputStream = mock[InputStream]\n\n        override val mockImageServicesTask = new ImageServicesTaskImpl {\n          override def createInputStream(uri: String) = mockInputStream\n\n          override def createBitmapByInputStream(is: InputStream) = mockBitmap\n        }\n\n        val result = mockImageServicesTask.getBitmapFromURL(uri).value.run\n        result shouldEqual Right(mockBitmap)\n      }\n\n    \"return a BitmapTransformationException with an invalid uri\" in\n      new ImageServicesTasksScope {\n\n        override val mockImageServicesTask = new ImageServicesTaskImpl {\n          override def createInputStream(uri: String) = javaNull\n        }\n\n        val result = mockImageServicesTask.getBitmapFromURL(uri).value.run\n        result must beAnInstanceOf[Left[BitmapTransformationException, _]]\n      }\n\n    \"successfuly saves the bitmap in the file\" in\n      new ImageServicesTasksScope {\n\n        val mockFileOutputStream = mock[FileOutputStream]\n\n        override val mockImageServicesTask = new ImageServicesTaskImpl {\n          override def createFileOutputStream(file: File): FileOutputStream = mockFileOutputStream\n        }\n\n        val result = mockImageServicesTask.saveBitmap(mockFile, mockBitmap).value.run\n        result shouldEqual Right((): Unit)\n        there was one(mockBitmap).compress(Bitmap.CompressFormat.PNG, 90, mockFileOutputStream)\n      }\n\n    \"return a FileException when the bitmap can not be saved\" in\n      new ImageServicesTasksScope {\n\n        override val mockImageServicesTask = new ImageServicesTaskImpl {\n          override def createFileOutputStream(file: File): FileOutputStream = javaNull\n        }\n        val result = mockImageServicesTask.saveBitmap(mockFile, mockBitmap).value.run\n        result must beAnInstanceOf[Left[FileException, _]]\n      }\n\n    \"successfully decodes the bitmap\" in\n      new ImageServicesTasksScope {\n\n        val shortcutIconResource = new ShortcutIconResource\n        shortcutIconResource.packageName = \"packageName\"\n        shortcutIconResource.resourceName = \"resourceName\"\n\n        packageManager.getResourcesForApplication(anyString) returns mockResources\n        mockResources.getIdentifier(any, any, any) returns 1\n\n        override val mockImageServicesTask = new ImageServicesTaskImpl {\n          override def createBitmapByResource(resources: Resources, id: Int): Bitmap = mockBitmap\n        }\n\n        val result = mockImageServicesTask\n          .getBitmapFromShortcutIconResource(shortcutIconResource)(contextSupport)\n          .value\n          .run\n        result shouldEqual Right(mockBitmap)\n      }\n\n    \"return a FileException when the bitmap can not be decoded\" in\n      new ImageServicesTasksScope {\n\n        val shortcutIconResource = new ShortcutIconResource\n        shortcutIconResource.packageName = \"packageName\"\n        shortcutIconResource.resourceName = \"resourceName\"\n\n        override val mockImageServicesTask = new ImageServicesTaskImpl {\n          override def createBitmapByResource(resources: Resources, id: Int): Bitmap = javaNull\n        }\n\n        val result = mockImageServicesTask\n          .getBitmapFromShortcutIconResource(shortcutIconResource)(contextSupport)\n          .value\n          .run\n        result must beAnInstanceOf[Left[FileException, _]]\n      }\n  }\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/intents/impl/LauncherIntentServicesImplData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.intents.impl\n\nimport cards.nine.models._\n\ntrait LauncherIntentServicesImplData {\n\n  val runtimeException  = new RuntimeException(\"Irrelevant Message\")\n  val securityException = new SecurityException(\"Irrelevant Message\")\n\n  val packageName   = \"package.name\"\n  val className     = \"class.Name\"\n  val googlePlayUrl = \"http://googlePlayUrl\"\n  val url           = \"http://mockUrl\"\n  val lookupKey     = \"lookupKey\"\n  val email         = \"email@google.com\"\n  val titleDialog   = \"Dialog Tile\"\n  val phoneNumber   = \"666 66 66 66\"\n  val shareText     = \"Share text\"\n\n  val appAction             = AppAction(packageName, className)\n  val appGooglePlayAction   = AppGooglePlayAction(googlePlayUrl, packageName)\n  val appLauncherAction     = AppLauncherAction(packageName)\n  val appSettingsAction     = AppSettingsAction(packageName)\n  val appUninstallAction    = AppUninstallAction(packageName)\n  val contactAction         = ContactAction(lookupKey)\n  val emailAction           = EmailAction(email, titleDialog)\n  val globalSettingsAction  = GlobalSettingsAction\n  val googlePlayStoreAction = GooglePlayStoreAction\n  val googleWeatherAction   = GoogleWeatherAction\n  val phoneSmsAction        = PhoneSmsAction(phoneNumber)\n  val phoneCallAction       = PhoneCallAction(phoneNumber)\n  val phoneDialAction       = PhoneDialAction(Some(phoneNumber))\n  val searchGlobalAction    = SearchGlobalAction\n  val searchVoiceAction     = SearchVoiceAction\n  val searchWebAction       = SearchWebAction\n  val shareAction           = ShareAction(shareText, titleDialog)\n  val urlAction             = UrlAction(url)\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/intents/impl/LauncherIntentServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.intents.impl\n\nimport android.app.Activity\nimport android.content.Intent\nimport cards.nine.commons.contexts.ActivityContextSupport\nimport cards.nine.commons.services.TaskService.NineCardException\nimport cards.nine.services.intents.{\n  IntentLauncherServicesException,\n  IntentLauncherServicesPermissionException\n}\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cats.syntax.either._\n\ntrait LauncherIntentServicesImplSpecification\n    extends Specification\n    with Mockito\n    with LauncherIntentServicesImplData {\n\n  trait LauncherIntentServicesImplScope extends Scope {\n\n    val mockContextSupport = mock[ActivityContextSupport]\n\n    val mockActivity = mock[Activity]\n\n    val mockIntentCreator = mock[IntentCreator]\n\n    val services = new LauncherIntentServicesImpl {\n      override val intentCreator: IntentCreator = mockIntentCreator\n    }\n\n    val mockIntent = mock[Intent]\n\n  }\n\n  trait WithActivity { self: LauncherIntentServicesImplScope =>\n\n    mockContextSupport.getActivity returns Some(mockActivity)\n\n  }\n\n}\n\nclass LauncherIntentServicesImplSpec extends LauncherIntentServicesImplSpecification {\n\n  \"launchIntentAction\" should {\n\n    \"call to startActivity with the Intent created by IntentCreator for an AppAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createAppIntent(any, any) returns mockIntent\n        val result = services.launchIntentAction(appAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createAppIntent(packageName, className)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for an AppGooglePlayAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createAppGooglePlayIntent(any, any) returns mockIntent\n        val result = services.launchIntentAction(appGooglePlayAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createAppGooglePlayIntent(googlePlayUrl, packageName)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for an AppLauncherAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createAppLaunchIntent(any)(any) returns mockIntent\n        val result = services.launchIntentAction(appLauncherAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createAppLaunchIntent(packageName)(mockContextSupport)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for an AppSettingsAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createAppSettingsIntent(any) returns mockIntent\n        val result = services.launchIntentAction(appSettingsAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createAppSettingsIntent(packageName)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for an AppUninstallAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createAppUninstallIntent(any) returns mockIntent\n        val result = services.launchIntentAction(appUninstallAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createAppUninstallIntent(packageName)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a ContactAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createContactIntent(any) returns mockIntent\n        val result = services.launchIntentAction(contactAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createContactIntent(lookupKey)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a EmailAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createEmailIntent(any, any) returns mockIntent\n        val result = services.launchIntentAction(emailAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createEmailIntent(email, titleDialog)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a GlobalSettingsAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createGlobalSettingsIntent() returns mockIntent\n        val result =\n          services.launchIntentAction(globalSettingsAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createGlobalSettingsIntent()\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a GooglePlayStoreAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createGooglePlayStoreIntent()(any) returns mockIntent\n        val result =\n          services.launchIntentAction(googlePlayStoreAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createGooglePlayStoreIntent()(mockContextSupport)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a GoogleWeatherAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createGoogleWeatherIntent() returns mockIntent\n        val result = services.launchIntentAction(googleWeatherAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createGoogleWeatherIntent()\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a PhoneCallAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createPhoneCallIntent(any) returns mockIntent\n        val result = services.launchIntentAction(phoneCallAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createPhoneCallIntent(phoneNumber)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a PhoneDialAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createPhoneDialIntent(any) returns mockIntent\n        val result = services.launchIntentAction(phoneDialAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createPhoneDialIntent(phoneDialAction.maybePhoneNumber)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a PhoneSmsAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createPhoneSmsIntent(any) returns mockIntent\n        val result = services.launchIntentAction(phoneSmsAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createPhoneSmsIntent(phoneNumber)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a SearchGlobalAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createSearchGlobalIntent() returns mockIntent\n        val result = services.launchIntentAction(searchGlobalAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createSearchGlobalIntent()\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a SearchVoiceAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createSearchVoiceIntent() returns mockIntent\n        val result = services.launchIntentAction(searchVoiceAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createSearchVoiceIntent()\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a SearchWebAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createSearchWebIntent() returns mockIntent\n        val result = services.launchIntentAction(searchWebAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createSearchWebIntent()\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a ShareAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createShareIntent(any, any) returns mockIntent\n        val result = services.launchIntentAction(shareAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createShareIntent(shareText, titleDialog)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"call to startActivity with the Intent created by IntentCreator for a UrlAction\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockIntentCreator.createUrlViewIntent(any) returns mockIntent\n        val result = services.launchIntentAction(urlAction)(mockContextSupport).value.run\n        result shouldEqual Right((): Unit)\n\n        there was one(mockIntentCreator).createUrlViewIntent(url)\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n  }\n\n  \"withActivity\" should {\n\n    \"execute the function if the ActivityContextSupport.getActivity returns Some of Activity\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        val right  = Right[NineCardException, Unit]((): Unit)\n        val result = services.withActivity(_ => right)(mockContextSupport)\n        result should be(right)\n\n        there was one(mockContextSupport).getActivity\n      }\n\n    \"return a Left[IntentLauncherServicesException] if the ActivityContextSupport.getActivity returns None\" in\n      new LauncherIntentServicesImplScope {\n\n        mockContextSupport.getActivity returns None\n        val right  = Right[NineCardException, Unit]((): Unit)\n        val result = services.withActivity(_ => right)(mockContextSupport)\n        result should beAnInstanceOf[Left[IntentLauncherServicesException, _]]\n\n        there was one(mockContextSupport).getActivity\n      }\n\n  }\n\n  \"launchIntent\" should {\n\n    \"return a Left[IntentLauncherServicesException] if Activity.startActivity throws a RuntimeException\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockActivity.startActivity(any) throws runtimeException\n        val result = services.launchIntent(mockIntent)(mockContextSupport).value.run\n        result should beAnInstanceOf[Left[IntentLauncherServicesException, _]]\n\n        there was one(mockContextSupport).getActivity\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n    \"return a Left[IntentLauncherServicesPermissionException] if Activity.startActivity throws a SecurityException\" in\n      new LauncherIntentServicesImplScope with WithActivity {\n\n        mockActivity.startActivity(any) throws securityException\n        val result = services.launchIntent(mockIntent)(mockContextSupport).value.run\n        result should beAnInstanceOf[Left[IntentLauncherServicesPermissionException, _]]\n\n        there was one(mockContextSupport).getActivity\n        there was one(mockActivity).startActivity(mockIntent)\n      }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/data/AppPersistenceServicesData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.data\n\nimport cards.nine.commons.test.data.ApplicationValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.models.{IterableAppCursor, IterableCursor}\nimport cards.nine.repository.model.{App, AppData, DataCounter}\nimport cards.nine.services.persistence.conversions.AppConversions\n\nimport scala.util.Random\n\ntrait AppPersistenceServicesData extends AppConversions {\n\n  val termDataCounter: String = Random.nextString(1)\n  val countDataCounter: Int   = Random.nextInt(2)\n\n  def repoAppData(num: Int = 0) =\n    AppData(\n      name = applicationName + num,\n      packageName = applicationPackageName + num,\n      className = applicationClassName + num,\n      category = categoryStr,\n      dateInstalled = dateInstalled,\n      dateUpdate = dateUpdated,\n      version = version,\n      installedFromGooglePlay = installedFromGooglePlay)\n\n  val repoAppData: AppData         = repoAppData(0)\n  val seqRepoAppData: Seq[AppData] = Seq(repoAppData(0), repoAppData(1), repoAppData(2))\n\n  def repoApp(num: Int = 0) = App(id = applicationId + num, data = repoAppData(num))\n\n  val repoApp: App         = repoApp(0)\n  val seqRepoApp: Seq[App] = Seq(repoApp(0), repoApp(1), repoApp(2))\n\n  val iterableCursorApp = new IterableCursor[App] {\n    override def count(): Int = seqRepoApp.length\n\n    override def moveToPosition(pos: Int): App = seqRepoApp(pos)\n\n    override def close(): Unit = ()\n  }\n\n  val iterableApps = new IterableAppCursor(iterableCursorApp, toApp)\n\n  def createDataCounter(i: Int): DataCounter =\n    DataCounter(term = s\"$i - $termDataCounter\", count = countDataCounter)\n\n  val dataCounters   = 1 to 10 map createDataCounter\n  val appPackageName = applicationPackageName + 0\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/data/CardPersistenceServicesData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.fortysevendeg.ninecardslauncher.services.persistence.data\n\nimport cards.nine.commons.test.data.CardValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.repository.model.{Card, CardData}\n\ntrait CardPersistenceServicesData {\n\n  def repoCardData(num: Int = 0) =\n    CardData(\n      position = cardPosition + num,\n      term = term,\n      packageName = Option(cardPackageName + num),\n      cardType = cardType,\n      intent = intent,\n      imagePath = Option(cardImagePath),\n      notification = Option(notification))\n\n  val repoCardData: CardData         = repoCardData(0)\n  val seqRepoCardData: Seq[CardData] = Seq(repoCardData(0), repoCardData(1), repoCardData(2))\n\n  def repoCard(num: Int = 0) = Card(id = cardId + num, data = repoCardData(num))\n\n  val repoCard: Card         = repoCard(0)\n  val seqRepoCard: Seq[Card] = Seq(repoCard(0), repoCard(1), repoCard(2))\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/data/CollectionPersistenceServicesData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.data\n\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.repository.model.{Collection, CollectionData}\n\ntrait CollectionPersistenceServicesData {\n\n  def repoCollectionData(num: Int = 0) =\n    CollectionData(\n      position = collectionPosition + num,\n      name = collectionName + num,\n      collectionType = collectionType.name,\n      icon = icon,\n      themedColorIndex = themedColorIndex,\n      appsCategory = Option(categoryStr),\n      originalSharedCollectionId = Option(originalSharedCollectionId),\n      sharedCollectionId = Option(sharedCollectionId),\n      sharedCollectionSubscribed = Option(sharedCollectionSubscribed))\n\n  val repoCollectionData: CollectionData = repoCollectionData(0)\n  val seqRepoCollectionData =\n    Seq(repoCollectionData(0), repoCollectionData(1), repoCollectionData(2))\n\n  def repoCollection(num: Int = 0) = Collection(id = collectionId + num, data = repoCollectionData)\n\n  val repoCollection: Collection = repoCollection(0)\n  val seqRepoCollection          = Seq(repoCollection(0), repoCollection(1), repoCollection(2))\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/data/DockAppPersistenceServicesData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.data\n\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.DockAppValues._\nimport cards.nine.models.IterableCursor\nimport cards.nine.repository.model.{DockApp, DockAppData}\n\ntrait DockAppPersistenceServicesData {\n\n  def repoDockAppData(num: Int = 0) =\n    DockAppData(\n      name = dockAppName,\n      dockType = dockType,\n      intent = intent,\n      imagePath = dockAppImagePath,\n      position = dockAppPosition + num)\n\n  val repoDockAppData: DockAppData = repoDockAppData(0)\n  val seqRepoDockAppData: Seq[DockAppData] =\n    Seq(repoDockAppData(0), repoDockAppData(1), repoDockAppData(2))\n\n  def repoDockApp(num: Int = 0) = DockApp(id = dockAppId + num, data = repoDockAppData(num))\n\n  val repoDockApp: DockApp         = repoDockApp(0)\n  val seqRepoDockApp: Seq[DockApp] = Seq(repoDockApp(0), repoDockApp(1), repoDockApp(2))\n\n  val iterableCursorDockApps = new IterableCursor[DockApp] {\n    override def count(): Int                      = seqRepoDockApp.length\n    override def moveToPosition(pos: Int): DockApp = seqRepoDockApp(pos)\n    override def close(): Unit                     = ()\n  }\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/data/MomentPersistenceServicesData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.fortysevendeg.ninecardslauncher.services.persistence.data\n\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.MomentValues._\nimport cards.nine.repository.model.{Moment, MomentData}\n\ntrait MomentPersistenceServicesData {\n\n  def repoMomentData(num: Int = 0) =\n    MomentData(\n      collectionId = Option(momentCollectionId + num),\n      timeslot = timeslotJson,\n      wifi = Seq(wifiSeq(num)).mkString(\",\"),\n      bluetooth = Seq(bluetoothSeq(num)).mkString(\",\"),\n      headphone = headphone,\n      momentType = Option(momentTypeSeq(num)))\n\n  val repoMomentData: MomentData = repoMomentData(0)\n  val seqRepoMomentData          = Seq(repoMomentData(0), repoMomentData(1), repoMomentData(2))\n\n  def repoMoment(num: Int = 0) = Moment(id = momentId + num, data = repoMomentData(num))\n\n  val repoMoment: Moment = repoMoment(0)\n  val seqRepoMoment      = Seq(repoMoment(0), repoMoment(1), repoMoment(2))\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/data/UserPersistenceServicesData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.data\n\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.repository.model.{User, UserData}\n\ntrait UserPersistenceServicesData {\n\n  def repoUserData(num: Int = 0) =\n    UserData(\n      email = Option(email),\n      sessionToken = Option(sessionToken),\n      apiKey = Option(apiKey),\n      deviceToken = Option(deviceToken),\n      marketToken = Option(marketToken),\n      deviceName = Option(userDeviceName),\n      deviceCloudId = Option(deviceCloudId),\n      name = Option(userName),\n      avatar = Option(avatar),\n      cover = Option(cover))\n\n  val repoUserData: UserData         = repoUserData(0)\n  val seqRepoUserData: Seq[UserData] = Seq(repoUserData(0), repoUserData(1), repoUserData(2))\n\n  def repoUser(num: Int = 0) = User(id = userId + num, data = repoUserData(num))\n\n  val repoUser: User         = repoUser(0)\n  val seqRepoUser: Seq[User] = Seq(repoUser(0), repoUser(1), repoUser(2))\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/data/WidgetPersistenceServicesData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.fortysevendeg.ninecardslauncher.services.persistence.data\n\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.WidgetValues._\nimport cards.nine.repository.model.{Widget, WidgetData}\n\ntrait WidgetPersistenceServicesData {\n\n  def repoWidgetData(num: Int = 0) =\n    WidgetData(\n      momentId = widgetMomentId,\n      packageName = widgetPackageName,\n      className = widgetClassName,\n      appWidgetId = appWidgetId,\n      startX = startX,\n      startY = startY,\n      spanX = spanX,\n      spanY = spanY,\n      widgetType = widgetType,\n      label = Option(label),\n      imagePath = Option(widgetImagePath),\n      intent = Option(intent))\n\n  val repoWidgetData: WidgetData = repoWidgetData(0)\n  val seqRepoWidgetData          = Seq(repoWidgetData(0), repoWidgetData(1), repoWidgetData(2))\n\n  def repoWidget(num: Int = 0) = Widget(id = widgetId + num, data = repoWidgetData(num))\n\n  val repoWidget: Widget = repoWidget(0)\n  val seqRepoWidget      = Seq(repoWidget(0), repoWidget(1), repoWidget(2))\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/impl/AppPersistenceServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.ApplicationTestData\nimport cards.nine.commons.test.data.ApplicationValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.models.Application\nimport cards.nine.models.types.{OrderByCategory, OrderByInstallDate, OrderByName}\nimport cards.nine.repository.RepositoryException\nimport cards.nine.repository.provider.AppEntity\nimport cards.nine.services.persistence.data.AppPersistenceServicesData\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mutable.Specification\n\ntrait AppPersistenceServicesSpecSpecification\n    extends Specification\n    with DisjunctionMatchers\n    with AppPersistenceServicesData\n    with RepositoryServicesScope {\n\n  trait AppPersistenceServicesScope\n      extends RepositoryServicesScope\n      with ApplicationTestData\n      with AppPersistenceServicesData {\n\n    val exception = RepositoryException(\"Irrelevant message\")\n\n  }\n\n}\n\nclass AppPersistenceServicesImplSpec extends AppPersistenceServicesSpecSpecification {\n\n  \"fetchApps\" should {\n\n    \"return a sequence of the apps when pass OrderByName\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchApps(any) returns TaskService(Task(Either.right(seqRepoApp)))\n      val result = persistenceServices.fetchApps(OrderByName, ascending = true).value.run\n      result shouldEqual Right(seqApplication)\n      there was one(mockAppRepository).fetchApps(contain(AppEntity.name))\n    }\n\n    \"return a sequence of the apps when pass OrderByName and descending order\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchApps(any) returns TaskService(Task(Either.right(seqRepoApp)))\n      val result = persistenceServices.fetchApps(OrderByName, ascending = false).value.run\n      result shouldEqual Right(seqApplication)\n      there was one(mockAppRepository).fetchApps(contain(AppEntity.name).and(contain(\"DESC\")))\n    }\n\n    \"return a sequence of the apps when pass OrderByInstallDate\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchApps(any) returns TaskService(Task(Either.right(seqRepoApp)))\n      val result = persistenceServices.fetchApps(OrderByInstallDate, ascending = true).value.run\n      result shouldEqual Right(seqApplication)\n      there was one(mockAppRepository).fetchApps(contain(AppEntity.dateInstalled))\n    }\n\n    \"return a sequence of the apps when pass OrderByCategory\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchApps(any) returns TaskService(Task(Either.right(seqRepoApp)))\n      val result = persistenceServices.fetchApps(OrderByCategory, ascending = true).value.run\n      result shouldEqual Right(seqApplication)\n      there was one(mockAppRepository).fetchApps(contain(AppEntity.category))\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchApps(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.fetchApps(OrderByName).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"findAppByPackage\" should {\n\n    \"return an App when a valid packageName is provided\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchAppByPackage(any) returns TaskService(\n        Task(Either.right(Option(repoApp))))\n      val result = persistenceServices.findAppByPackage(appPackageName).value.run\n\n      result must beLike {\n        case Right(maybeApp) =>\n          maybeApp must beSome[Application].which { app =>\n            app.id shouldEqual applicationId\n            app.packageName shouldEqual appPackageName\n          }\n      }\n    }\n\n    \"return None when an invalid packageName is provided\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchAppByPackage(any) returns TaskService(Task(Either.right(None)))\n      val result =\n        persistenceServices.findAppByPackage(nonExistentApplicationPackageName).value.run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchAppByPackage(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.findAppByPackage(appPackageName).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchAppByPackages\" should {\n\n    \"return a Seq App when a valid packageName is provided\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchAppByPackages(any) returns TaskService(\n        Task(Either.right(Seq(repoApp))))\n      val result = persistenceServices.fetchAppByPackages(Seq(appPackageName)).value.run\n\n      result must beLike {\n        case Right(applicationSeq) =>\n          applicationSeq shouldEqual Seq(application)\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchAppByPackages(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.fetchAppByPackages(Seq(appPackageName)).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"addApp\" should {\n\n    \"return a App value for a valid request\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.addApp(any) returns TaskService(Task(Either.right(repoApp)))\n      val result = persistenceServices.addApp(applicationData).value.run\n\n      result must beLike {\n        case Right(app) =>\n          app.id shouldEqual applicationId\n          app.packageName shouldEqual appPackageName\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.addApp(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.addApp(applicationData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"addApps\" should {\n\n    \"return Unit for a valid request\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.addApps(any) returns TaskService(Task(Either.right(())))\n      val result = persistenceServices.addApps(seqApplicationData).value.run\n      result shouldEqual Right((): Unit)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.addApps(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.addApps(seqApplicationData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteAllApps\" should {\n\n    \"return the number of elements deleted for a valid request\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.deleteApps() returns TaskService(Task(Either.right(deletedApplications)))\n      val result = persistenceServices.deleteAllApps().value.run\n      result shouldEqual Right(deletedApplications)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.deleteApps() returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteAllApps().value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteAppsById\" should {\n\n    \"return the number of elements deleted for a valid request\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.deleteApps(any) returns TaskService(Task(Either.right(deletedApplication)))\n      val result = persistenceServices.deleteAppsByIds(Seq.empty).value.run\n      result shouldEqual Right(deletedApplication)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.deleteApps(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteAppsByIds(Seq.empty).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteAppByPackage\" should {\n\n    \"return the number of elements deleted for a valid request\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.deleteAppByPackage(any) returns TaskService(\n        Task(Either.right(deletedApplication)))\n      val result = persistenceServices.deleteAppByPackage(applicationPackageName).value.run\n      result shouldEqual Right(1)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.deleteAppByPackage(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteAppByPackage(applicationPackageName).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"updateApp\" should {\n\n    \"return the number of elements updated for a valid request\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.updateApp(any) returns TaskService(Task(Either.right(updatedApplication)))\n      val result = persistenceServices.updateApp(application).value.run\n      result shouldEqual Right(1)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.updateApp(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.updateApp(application).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchIterableApps\" should {\n\n    \"return a iterable of apps when pass OrderByName\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchIterableApps(any, any, any) returns TaskService(\n        Task(Either.right(iterableCursorApp)))\n      val result = persistenceServices.fetchIterableApps(OrderByName, ascending = true).value.run\n\n      result must beLike {\n        case Right(iter) => iter.moveToPosition(0) shouldEqual iterableApps.moveToPosition(0)\n      }\n    }\n\n    \"return a iterable of apps when pass OrderByInstallDate\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchIterableApps(any, any, any) returns TaskService(\n        Task(Either.right(iterableCursorApp)))\n      val result =\n        persistenceServices.fetchIterableApps(OrderByInstallDate, ascending = true).value.run\n\n      result must beLike {\n        case Right(iter) => iter.moveToPosition(0) shouldEqual iterableApps.moveToPosition(0)\n      }\n    }\n\n    \"return a iterable of apps when pass OrderByCategory\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchIterableApps(any, any, any) returns TaskService(\n        Task(Either.right(iterableCursorApp)))\n      val result =\n        persistenceServices.fetchIterableApps(OrderByCategory, ascending = true).value.run\n\n      result must beLike {\n        case Right(iter) => iter.moveToPosition(0) shouldEqual iterableApps.moveToPosition(0)\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchIterableApps(any, any, any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.fetchIterableApps(OrderByName).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchIterableAppsByKeyword\" should {\n\n    \"return a iterable of apps when pass a keyword and OrderByName\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchIterableApps(any, any, any) returns TaskService(\n        Task(Either.right(iterableCursorApp)))\n      val result = persistenceServices\n        .fetchIterableAppsByKeyword(keyword, OrderByName, ascending = true)\n        .value\n        .run\n\n      result must beLike {\n        case Right(iter) => iter.moveToPosition(0) shouldEqual iterableApps.moveToPosition(0)\n      }\n    }\n\n    \"return a iterable of apps when pass a keyword and OrderByInstallDate\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchIterableApps(any, any, any) returns TaskService(\n        Task(Either.right(iterableCursorApp)))\n      val result = persistenceServices\n        .fetchIterableAppsByKeyword(keyword, OrderByInstallDate, ascending = true)\n        .value\n        .run\n      result must beLike {\n        case Right(iter) => iter.moveToPosition(0) shouldEqual iterableApps.moveToPosition(0)\n      }\n    }\n\n    \"return a iterable of apps when pass a keyword and OrderByCategory\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchIterableApps(any, any, any) returns TaskService(\n        Task(Either.right(iterableCursorApp)))\n      val result = persistenceServices\n        .fetchIterableAppsByKeyword(keyword, OrderByCategory, ascending = true)\n        .value\n        .run\n\n      result must beLike {\n        case Right(iter) => iter.moveToPosition(0) shouldEqual iterableApps.moveToPosition(0)\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchIterableApps(any, any, any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.fetchIterableAppsByKeyword(keyword, OrderByName).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchAppsByCategory\" should {\n\n    \"return a sequence of apps when pass a category and OrderByName\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchAppsByCategory(any, any) returns TaskService(\n        Task(Either.right(seqRepoApp)))\n      val result = persistenceServices\n        .fetchAppsByCategory(categoryStr, OrderByName, ascending = true)\n        .value\n        .run\n      result shouldEqual Right(seqApplication)\n      there was one(mockAppRepository)\n        .fetchAppsByCategory(contain(categoryStr), contain(AppEntity.name))\n    }\n\n    \"return a sequence of apps when pass a category and OrderByInstallDate\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchAppsByCategory(any, any) returns TaskService(\n        Task(Either.right(seqRepoApp)))\n      val result = persistenceServices\n        .fetchAppsByCategory(categoryStr, OrderByInstallDate, ascending = true)\n        .value\n        .run\n      result shouldEqual Right(seqApplication)\n      there was one(mockAppRepository)\n        .fetchAppsByCategory(contain(categoryStr), contain(AppEntity.dateInstalled))\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchAppsByCategory(any, any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.fetchAppsByCategory(categoryStr, OrderByName).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchIterableAppsByCategory\" should {\n\n    \"return a iterable of apps when pass a category and OrderByName\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchIterableAppsByCategory(any, any) returns TaskService(\n        Task(Either.right(iterableCursorApp)))\n      val result = persistenceServices\n        .fetchIterableAppsByCategory(categoryStr, OrderByName, ascending = true)\n        .value\n        .run\n\n      result must beLike {\n        case Right(iter) => iter.moveToPosition(0) shouldEqual iterableApps.moveToPosition(0)\n      }\n    }\n\n    \"return a iterable of apps when pass a category and OrderByInstallDate\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchIterableAppsByCategory(any, any) returns TaskService(\n        Task(Either.right(iterableCursorApp)))\n      val result = persistenceServices\n        .fetchIterableAppsByCategory(categoryStr, OrderByInstallDate, ascending = true)\n        .value\n        .run\n\n      result must beLike {\n        case Right(iter) => iter.moveToPosition(0) shouldEqual iterableApps.moveToPosition(0)\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchIterableAppsByCategory(any, any) returns TaskService(\n        Task(Either.left(exception)))\n      val result =\n        persistenceServices.fetchIterableAppsByCategory(categoryStr, OrderByName).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchAlphabeticalAppsCounter\" should {\n\n    \"return a sequence of DataCounter sort alphabetically\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchAlphabeticalAppsCounter returns TaskService(\n        Task(Either.right(dataCounters)))\n      val result = persistenceServices.fetchAlphabeticalAppsCounter.value.run\n\n      result must beLike {\n        case Right(counters) => counters map (_.term) shouldEqual (dataCounters map (_.term))\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchAlphabeticalAppsCounter returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.fetchAlphabeticalAppsCounter.value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchCategorizedAppsCounter\" should {\n\n    \"return a sequence of DataCounter by category sort alphabetically\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchCategorizedAppsCounter returns TaskService(\n        Task(Either.right(dataCounters)))\n      val result = persistenceServices.fetchCategorizedAppsCounter.value.run\n      result must beLike {\n        case Right(counters) => counters map (_.term) shouldEqual (dataCounters map (_.term))\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchCategorizedAppsCounter returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.fetchCategorizedAppsCounter.value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchInstallationDateAppsCounter\" should {\n\n    \"return a sequence of DataCounter by installation date\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchInstallationDateAppsCounter returns TaskService(\n        Task(Either.right(dataCounters)))\n      val result = persistenceServices.fetchInstallationDateAppsCounter.value.run\n      result must beLike {\n        case Right(counters) => counters map (_.term) shouldEqual (dataCounters map (_.term))\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new AppPersistenceServicesScope {\n\n      mockAppRepository.fetchInstallationDateAppsCounter returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.fetchInstallationDateAppsCounter.value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/impl/CardPersistenceServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.CardTestData\nimport cards.nine.commons.test.data.CardValues._\nimport cards.nine.models.Card\nimport cards.nine.repository.RepositoryException\nimport cards.nine.repository.provider.CardEntity\nimport cats.syntax.either._\nimport com.fortysevendeg.ninecardslauncher.services.persistence.data.CardPersistenceServicesData\nimport monix.eval.Task\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mutable.Specification\n\ntrait CardPersistenceServicesDataSpecification extends Specification with DisjunctionMatchers {\n\n  trait CardServicesScope\n      extends RepositoryServicesScope\n      with CardTestData\n      with CardPersistenceServicesData {\n\n    val exception = RepositoryException(\"Irrelevant message\")\n\n  }\n\n}\n\nclass CardPersistenceServicesImplSpec extends CardPersistenceServicesDataSpecification {\n\n  \"addCard\" should {\n\n    \"return a Card value for a valid request\" in new CardServicesScope {\n\n      mockCardRepository.addCard(any, any) returns TaskService(Task(Either.right(repoCard)))\n      val result = persistenceServices.addCard(cardCollectionId, cardData).value.run\n\n      result must beLike {\n        case Right(card) =>\n          card.id shouldEqual cardId\n          card.cardType.name shouldEqual cardType\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CardServicesScope {\n\n      mockCardRepository.addCard(any, any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.addCard(cardCollectionId, cardData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteAllCards\" should {\n\n    \"return the number of elements deleted for a valid request\" in new CardServicesScope {\n\n      mockCardRepository.deleteCards(any, any) returns TaskService(\n        Task(Either.right(seqRepoCard.size)))\n      val result = persistenceServices.deleteAllCards().value.run\n      result shouldEqual Right(seqRepoCard.size)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CardServicesScope {\n\n      mockCardRepository.deleteCards(any, any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteAllCards().value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteCard\" should {\n\n    \"return the number of elements deleted for a valid request\" in new CardServicesScope {\n\n      seqRepoCard foreach { repoCard =>\n        mockCardRepository.deleteCard(cardCollectionId, repoCard.id) returns TaskService(\n          Task(Either.right(deletedCard)))\n      }\n\n      val result = persistenceServices.deleteCard(cardCollectionId, card.id).value.run\n      result shouldEqual Right(deletedCard)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CardServicesScope {\n\n      seqRepoCard foreach { repoCard =>\n        mockCardRepository.deleteCard(cardCollectionId, repoCard.id) returns TaskService(\n          Task(Either.left(exception)))\n      }\n\n      val result = persistenceServices.deleteCard(cardCollectionId, card.id).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteCards\" should {\n\n    \"return the number of elements deleted for a valid request\" in new CardServicesScope {\n\n      mockCardRepository.deleteCards(any, any) returns TaskService(\n        Task(Either.right(deletedCards)))\n      val result = persistenceServices.deleteCards(cardCollectionId, Seq(card.id)).value.run\n      result shouldEqual Right(deletedCards)\n\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CardServicesScope {\n\n      mockCardRepository.deleteCards(any, any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteCards(cardCollectionId, Seq(card.id)).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteCardsByCollection\" should {\n\n    \"return the number of elements deleted for a valid request\" in new CardServicesScope {\n\n      mockCardRepository.deleteCards(any, any) returns TaskService(\n        Task(Either.right(deletedCards)))\n      val result = persistenceServices.deleteCardsByCollection(cardCollectionId).value.run\n      result shouldEqual Right(deletedCards)\n      there was one(mockCardRepository)\n        .deleteCards(None, where = s\"${CardEntity.collectionId} = $cardCollectionId\")\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CardServicesScope {\n\n      mockCardRepository.deleteCards(any, any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteCardsByCollection(cardCollectionId).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n      there was one(mockCardRepository)\n        .deleteCards(None, where = s\"${CardEntity.collectionId} = $cardCollectionId\")\n    }\n  }\n\n  \"fetchCardsByCollection\" should {\n\n    \"return a list of Card elements for a valid request\" in new CardServicesScope {\n\n      List.tabulate(5) { index =>\n        mockCardRepository.fetchCardsByCollection(cardCollectionId + index) returns TaskService(\n          Task(Either.right(seqRepoCard)))\n      }\n\n      val result = persistenceServices.fetchCardsByCollection(cardCollectionId).value.run\n\n      result must beLike {\n        case Right(cards) => cards.size shouldEqual seqCard.size\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CardServicesScope {\n\n      List.tabulate(5) { index =>\n        mockCardRepository.fetchCardsByCollection(cardCollectionId + index) returns TaskService(\n          Task(Either.left(exception)))\n      }\n\n      val result = persistenceServices.fetchCardsByCollection(cardCollectionId).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchCards\" should {\n\n    \"return a list of Card elements for a valid request\" in new CardServicesScope {\n      mockCardRepository.fetchCards returns TaskService(Task(Either.right(seqRepoCard)))\n\n      val result = persistenceServices.fetchCards.value.run\n\n      result must beLike {\n        case Right(cards) => cards.size shouldEqual seqCard.size\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CardServicesScope {\n\n      mockCardRepository.fetchCards returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.fetchCards.value.run\n\n      result must beLike {\n        case Left(e) => e.cause must beSome.which(_ shouldEqual exception)\n      }\n    }\n  }\n\n  \"findCardById\" should {\n\n    \"return a Card for a valid request\" in new CardServicesScope {\n\n      mockCardRepository.findCardById(cardId) returns TaskService(\n        Task(Either.right(Option(repoCard))))\n      val result = persistenceServices.findCardById(cardId).value.run\n\n      result must beLike {\n        case Right(maybeCard) =>\n          maybeCard must beSome[Card].which { card =>\n            card.cardType.name shouldEqual cardType\n          }\n      }\n    }\n\n    \"return None when a non-existent id is given\" in new CardServicesScope {\n\n      mockCardRepository.findCardById(nonExistentCardId) returns TaskService(\n        Task(Either.right(None)))\n      val result = persistenceServices.findCardById(nonExistentCardId).value.run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CardServicesScope {\n\n      mockCardRepository.findCardById(cardId) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.findCardById(cardId).value.run\n\n      result must beLike {\n        case Left(e) => e.cause must beSome.which(_ shouldEqual exception)\n      }\n    }\n  }\n\n  \"updateCard\" should {\n\n    \"return the number of elements updated for a valid request\" in new CardServicesScope {\n\n      mockCardRepository.updateCard(any) returns TaskService(Task(Either.right(deletedCard)))\n      val result = persistenceServices.updateCard(card).value.run\n      result shouldEqual Right(deletedCard)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CardServicesScope {\n\n      mockCardRepository.updateCard(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.updateCard(card).value.run\n\n      result must beLike {\n        case Left(e) => e.cause must beSome.which(_ shouldEqual exception)\n      }\n    }\n  }\n\n  \"updateCards\" should {\n\n    \"return the sequence with the number of elements updated for a valid request\" in new CardServicesScope {\n\n      mockCardRepository.updateCards(any) returns TaskService(\n        Task(Either.right(deletedCard to deletedCards)))\n      val result = persistenceServices.updateCards(seqCard).value.run\n      result shouldEqual Right(deletedCard to deletedCards)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CardServicesScope {\n\n      mockCardRepository.updateCards(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.updateCards(seqCard).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/impl/CollectionPersistenceServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.CardValues._\nimport cards.nine.commons.test.data.CollectionTestData\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.commons.test.data.CommonValues._\nimport cards.nine.commons.test.data.MomentValues._\nimport cards.nine.models.Collection\nimport cards.nine.repository.RepositoryException\nimport cards.nine.repository.provider.CardEntity\nimport cards.nine.services.persistence.data.CollectionPersistenceServicesData\nimport cats.syntax.either._\nimport com.fortysevendeg.ninecardslauncher.services.persistence.data.{\n  CardPersistenceServicesData,\n  MomentPersistenceServicesData\n}\nimport monix.eval.Task\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\n\ntrait CollectionPersistenceServicesDataSpecification\n    extends Specification\n    with DisjunctionMatchers\n    with Mockito {\n\n  trait CollectionServicesResponses\n      extends RepositoryServicesScope\n      with CollectionTestData\n      with CollectionPersistenceServicesData\n      with CardPersistenceServicesData\n      with MomentPersistenceServicesData {\n\n    val exception = RepositoryException(\"Irrelevant message\")\n\n  }\n\n}\n\nclass CollectionPersistenceServicesImplSpec\n    extends CollectionPersistenceServicesDataSpecification {\n\n  \"addCollection\" should {\n\n    \"return a Collection value for a valid request\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.addCollection(any) returns TaskService(\n        Task(Either.right(repoCollection)))\n      mockCardRepository.addCards(any) returns TaskService(Task(Either.right(Seq(repoCard))))\n      mockMomentRepository.fetchMoments() returns TaskService(Task(Either.right(seqRepoMoment)))\n      mockMomentRepository.updateMoment(any) returns TaskService(Task(Either.right(updatedMoment)))\n\n      val result = persistenceServices.addCollection(collectionData).value.run\n\n      result must beLike {\n        case Right(collection) =>\n          collection.id shouldEqual collectionId\n          collection.collectionType shouldEqual collectionType\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.addCollection(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.addCollection(collectionData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"addCollections\" should {\n\n    \"return a Seq Collection value for a valid request\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.addCollections(any) returns TaskService(\n        Task(Either.right(seqRepoCollection)))\n      mockCardRepository.addCards(any) returns TaskService(Task(Either.right(Seq(repoCard))))\n      mockMomentRepository.fetchMoments() returns TaskService(Task(Either.right(seqRepoMoment)))\n      mockMomentRepository.updateMoment(any) returns TaskService(Task(Either.right(updatedMoment)))\n\n      val result = persistenceServices.addCollections(seqCollectionData).value.run\n      result must beLike {\n        case Right(collections) => collections.size shouldEqual seqCollectionData.size\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.addCollections(any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.addCollections(seqCollectionData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n\n  }\n\n  \"deleteAllCollections\" should {\n\n    \"return the number of elements deleted for a valid request\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.deleteCollections() returns TaskService(\n        Task(Either.right(deletedCollections)))\n      mockCardRepository.deleteCards(any, any) returns TaskService(\n        Task(Either.right(deletedCards)))\n\n      val result = persistenceServices.deleteAllCollections().value.run\n      result shouldEqual Right(deletedCollections)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.deleteCollections() returns TaskService(\n        Task(Either.left(exception)))\n      mockCardRepository.deleteCards(any, any) returns TaskService(Task(Either.left(exception)))\n\n      val result = persistenceServices.deleteAllCollections().value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteCollection\" should {\n\n    \"return the number of elements deleted for a valid request\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.deleteCollection(any) returns TaskService(\n        Task(Either.right(deletedCollection)))\n      mockCardRepository.deleteCards(any, any) returns TaskService(\n        Task(Either.right(deletedCards)))\n      mockMomentRepository.updateMoment(\n        repoMoment.copy(data = repoMoment.data.copy(collectionId = None))) returns TaskService(\n        Task(Either.right(updatedMoment)))\n\n      val result = persistenceServices.deleteCollection(collection).value.run\n      result shouldEqual Right(deletedCollection)\n      there was one(mockCardRepository)\n        .deleteCards(None, where = s\"${CardEntity.collectionId} = $collectionId\")\n    }\n\n    \"return Right of Unit if Moment in Request is None\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.deleteCollection(any) returns TaskService(\n        Task(Either.right(deletedCollection)))\n      mockCardRepository.deleteCards(any, any) returns TaskService(\n        Task(Either.right(deletedCards)))\n      mockMomentRepository.updateMoment(\n        repoMoment.copy(data = repoMoment.data.copy(collectionId = None))) returns TaskService(\n        Task(Either.right(updatedMoment)))\n\n      val result = persistenceServices.deleteCollection(collection.copy(moment = None)).value.run\n      result shouldEqual Right(deletedCollection)\n      there was one(mockCardRepository)\n        .deleteCards(None, where = s\"${CardEntity.collectionId} = $collectionId\")\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.deleteCollection(any) returns TaskService(\n        Task(Either.left(exception)))\n      mockCardRepository.deleteCards(any, any) returns TaskService(Task(Either.left(exception)))\n\n      val result = persistenceServices.deleteCollection(collection).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n      there was one(mockCardRepository)\n        .deleteCards(None, where = s\"${CardEntity.collectionId} = $collectionId\")\n    }\n  }\n\n  \"fetchCollectionByPosition\" should {\n\n    \"return a Collection for a valid request\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchCollectionByPosition(any) returns TaskService(\n        Task(Either.right(Option(repoCollection))))\n      List.tabulate(5) { index =>\n        mockCardRepository.fetchCardsByCollection(collectionId + index) returns TaskService(\n          Task(Either.right(seqRepoCard)))\n      }\n      mockMomentRepository.fetchMoments(any, any, any) returns TaskService(\n        Task(Either.right(seqRepoMoment)))\n\n      val result = persistenceServices.fetchCollectionByPosition(collectionPosition).value.run\n\n      result must beLike {\n        case Right(maybeCollection) =>\n          maybeCollection must beSome[Collection].which { collection =>\n            collection.id shouldEqual collectionId\n            collection.position shouldEqual collectionPosition\n          }\n      }\n    }\n\n    \"return None when a non-existent id is given\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchCollectionByPosition(any) returns TaskService(\n        Task(Either.right(None)))\n      val result =\n        persistenceServices.fetchCollectionByPosition(nonExistentCollectionPosition).value.run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchCollectionByPosition(any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.fetchCollectionByPosition(collectionPosition).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchCollectionBySharedCollectionId\" should {\n\n    \"return a Collection for a valid request\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchCollectionBySharedCollectionId(any) returns TaskService(\n        Task(Either.right(Option(repoCollection))))\n      List.tabulate(5) { index =>\n        mockCardRepository.fetchCardsByCollection(collectionId + index) returns TaskService(\n          Task(Either.right(seqRepoCard)))\n      }\n      mockMomentRepository.fetchMoments(any, any, any) returns TaskService(\n        Task(Either.right(seqRepoMoment)))\n\n      val result =\n        persistenceServices.fetchCollectionBySharedCollectionId(sharedCollectionId).value.run\n\n      result must beLike {\n        case Right(maybeCollection) =>\n          maybeCollection must beSome[Collection].which { collection =>\n            collection.id shouldEqual collectionId\n            collection.sharedCollectionId shouldEqual Option(sharedCollectionId)\n          }\n      }\n    }\n\n    \"return None when a non-existent id is given\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchCollectionBySharedCollectionId(any) returns TaskService(\n        Task(Either.right(None)))\n      val result = persistenceServices\n        .fetchCollectionBySharedCollectionId(nonExistentSharedCollectionId)\n        .value\n        .run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchCollectionBySharedCollectionId(any) returns TaskService(\n        Task(Either.left(exception)))\n      val result =\n        persistenceServices.fetchCollectionBySharedCollectionId(sharedCollectionId).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchCollectionsBySharedCollectionIds\" should {\n\n    \"return a sequence of collections for a valid request\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchCollectionsBySharedCollectionIds(any) returns TaskService(\n        Task(Either.right(Seq(repoCollection))))\n\n      val result = persistenceServices\n        .fetchCollectionsBySharedCollectionIds(Seq(sharedCollectionId))\n        .value\n        .run\n\n      result must beLike {\n        case Right(collections) =>\n          collections.headOption must beSome[Collection].which { collection =>\n            collection.id shouldEqual collectionId\n            collection.sharedCollectionId shouldEqual Option(sharedCollectionId)\n          }\n      }\n    }\n\n    \"return None when a non-existent id is given\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchCollectionsBySharedCollectionIds(any) returns TaskService(\n        Task(Either.right(Seq.empty)))\n      val result = persistenceServices\n        .fetchCollectionsBySharedCollectionIds(Seq(nonExistentSharedCollectionId))\n        .value\n        .run\n      result shouldEqual Right(Seq.empty)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchCollectionsBySharedCollectionIds(any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices\n        .fetchCollectionsBySharedCollectionIds(Seq(sharedCollectionId))\n        .value\n        .run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchCollections\" should {\n\n    \"return a list of Collection elements for a valid request\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchSortedCollections returns TaskService(\n        Task(Either.right(seqRepoCollection)))\n      List.tabulate(5) { index =>\n        mockCardRepository.fetchCardsByCollection(collectionId + index) returns TaskService(\n          Task(Either.right(seqRepoCard)))\n      }\n      mockMomentRepository.fetchMoments(any, any, any) returns TaskService(\n        Task(Either.right(seqRepoMoment)))\n\n      val result = persistenceServices.fetchCollections.value.run\n\n      result must beLike {\n        case Right(collections) => collections.size shouldEqual seqCollection.size\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchSortedCollections returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.fetchCollections.value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"findCollectionById\" should {\n\n    \"return a Collection for a valid request\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.findCollectionById(any) returns TaskService(\n        Task(Either.right(Option(repoCollection))))\n      List.tabulate(5) { index =>\n        mockCardRepository.fetchCardsByCollection(collectionId + index) returns TaskService(\n          Task(Either.right(seqRepoCard)))\n      }\n      mockMomentRepository.fetchMoments(any, any, any) returns TaskService(\n        Task(Either.right(seqRepoMoment)))\n\n      val result = persistenceServices.findCollectionById(collectionId).value.run\n\n      result must beLike {\n        case Right(maybeCollection) =>\n          maybeCollection must beSome[Collection].which { collection =>\n            collection.id shouldEqual collectionId\n          }\n      }\n    }\n\n    \"return None when a non-existent id is given\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.findCollectionById(any) returns TaskService(\n        Task(Either.right(None)))\n      val result = persistenceServices.findCollectionById(nonExistentCollectionId).value.run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.findCollectionById(any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.findCollectionById(collectionId).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"findCollectionByCategory\" should {\n\n    \"return a Collection for a valid request\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchCollectionsByCategory(any) returns TaskService(\n        Task(Either.right(seqRepoCollection)))\n      List.tabulate(5) { index =>\n        mockCardRepository.fetchCardsByCollection(collectionId + index) returns TaskService(\n          Task(Either.right(seqRepoCard)))\n      }\n      mockMomentRepository.fetchMoments(any, any, any) returns TaskService(\n        Task(Either.right(seqRepoMoment)))\n\n      val result = persistenceServices.findCollectionByCategory(categoryStr).value.run\n      result must beLike {\n        case Right(maybeCollection) =>\n          maybeCollection must beSome[Collection].which { collection =>\n            collection.appsCategory map (_.name) shouldEqual Some(categoryStr)\n          }\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchCollectionsByCategory(any) returns TaskService(\n        Task(Either.right(Seq.empty)))\n      val result = persistenceServices.findCollectionByCategory(categoryStr).value.run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.fetchCollectionsByCategory(any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.findCollectionByCategory(categoryStr).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"updateCollection\" should {\n\n    \"return the number of elements updated for a valid request\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.updateCollection(any) returns TaskService(\n        Task(Either.right(updatedCollection)))\n      val result = persistenceServices.updateCollection(collection).value.run\n      result shouldEqual Right(updatedCollection)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.updateCollection(any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.updateCollection(collection).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"updateCollections\" should {\n\n    \"return the number of elements updated for a valid request\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.updateCollections(any) returns TaskService(\n        Task(Either.right(Seq(updatedCollections))))\n      val result = persistenceServices.updateCollections(seqCollection).value.run\n      result shouldEqual Right(Seq(updatedCollections))\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new CollectionServicesResponses {\n\n      mockCollectionRepository.updateCollections(any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.updateCollections(seqCollection).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/impl/DockAppPersistenceServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.DockAppTestData\nimport cards.nine.commons.test.data.DockAppValues._\nimport cards.nine.models.DockApp\nimport cards.nine.repository.RepositoryException\nimport cards.nine.services.persistence.data.DockAppPersistenceServicesData\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mutable.Specification\n\ntrait DockAppPersistenceServicesSpecification extends Specification with DisjunctionMatchers {\n\n  trait DockAppPersistenceServices\n      extends RepositoryServicesScope\n      with DockAppTestData\n      with DockAppPersistenceServicesData {\n\n    val exception = RepositoryException(\"Irrelevant message\")\n\n  }\n\n}\n\nclass DockAppPersistenceServicesImplSpec extends DockAppPersistenceServicesSpecification {\n\n  \"createOrUpdateDockApp\" should {\n\n    \"return a DockApp value for a valid request adding a dockApp\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.fetchDockApps(any, any, any) returns TaskService(\n        Task(Either.right(Seq.empty)))\n\n      mockDockAppRepository.addDockApps(any) returns TaskService(\n        Task(Either.right(Seq(repoDockApp))))\n\n      mockDockAppRepository.updateDockApps(any) returns TaskService(Task(Either.right(Seq.empty)))\n\n      val result = persistenceServices.createOrUpdateDockApp(Seq(dockAppData)).value.run\n      result shouldEqual Right(Seq(dockApp))\n    }\n\n    \"return a DockApp value for a valid request updating a dockApp\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.fetchDockApps(any, any, any) returns TaskService(\n        Task(Either.right(seqRepoDockApp)))\n\n      mockDockAppRepository.addDockApps(any) returns TaskService(Task(Either.right(Seq.empty)))\n\n      mockDockAppRepository.updateDockApps(any) returns TaskService(\n        Task(Either.right(Seq(deletedDockApp))))\n\n      val result = persistenceServices.createOrUpdateDockApp(Seq(dockAppData)).value.run\n      result shouldEqual Right(Seq(dockApp))\n\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception fetching the dockApps\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.fetchDockApps(any, any, any) returns TaskService(\n        Task(Either.left(exception)))\n\n      val result = persistenceServices.createOrUpdateDockApp(seqDockAppData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception adding the dockApps\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.fetchDockApps(any, any, any) returns TaskService(\n        Task(Either.right(Seq.empty)))\n\n      mockDockAppRepository.addDockApps(any) returns TaskService(Task(Either.left(exception)))\n\n      val result = persistenceServices.createOrUpdateDockApp(seqDockAppData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception updating the dockApps\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.fetchDockApps(any, any, any) returns TaskService(\n        Task(Either.right(seqRepoDockApp)))\n\n      mockDockAppRepository.addDockApps(any) returns TaskService(Task(Either.right(Seq.empty)))\n\n      mockDockAppRepository.updateDockApps(any) returns TaskService(Task(Either.left(exception)))\n\n      val result = persistenceServices.createOrUpdateDockApp(seqDockAppData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteAllDockApps\" should {\n\n    \"return the number of elements deleted for a valid request\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.deleteDockApps() returns TaskService(\n        Task(Either.right(deletedDockApps)))\n      val result = persistenceServices.deleteAllDockApps().value.run\n\n      result must beLike {\n        case Right(deleted) => deleted shouldEqual deletedDockApps\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.deleteDockApps() returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteAllDockApps().value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteDockApp\" should {\n\n    \"return the number of elements deleted for a valid request\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.deleteDockApp(any) returns TaskService(\n        Task(Either.right(deletedDockApp)))\n      val result = persistenceServices.deleteDockApp(dockApp).value.run\n      result shouldEqual Right(deletedDockApp)\n\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.deleteDockApp(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteDockApp(dockApp).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchDockApps\" should {\n\n    \"return a list of DockApp elements for a valid request\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.fetchDockApps() returns TaskService(Task(Either.right(seqRepoDockApp)))\n      val result = persistenceServices.fetchDockApps.value.run\n\n      result must beLike {\n        case Right(dockAppItems) =>\n          dockAppItems.size shouldEqual seqDockApp.size\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.fetchDockApps() returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.fetchDockApps.value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"findDockAppById\" should {\n\n    \"return a DockApp for a valid request\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.findDockAppById(any) returns TaskService(\n        Task(Either.right(Option(repoDockApp))))\n      val result = persistenceServices.findDockAppById(dockAppId).value.run\n\n      result must beLike {\n        case Right(maybeDockApp) =>\n          maybeDockApp must beSome[DockApp].which { dockApp =>\n            dockApp.id shouldEqual dockAppId\n          }\n      }\n    }\n\n    \"return None when a non-existent id is given\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.findDockAppById(any) returns TaskService(Task(Either.right(None)))\n      val result = persistenceServices.findDockAppById(nonExistentDockAppId).value.run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new DockAppPersistenceServices {\n\n      mockDockAppRepository.findDockAppById(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.findDockAppById(dockAppId).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/impl/MomentPersistenceServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.MomentTestData\nimport cards.nine.commons.test.data.CollectionValues._\nimport cards.nine.commons.test.data.MomentValues._\nimport cards.nine.models.Moment\nimport cards.nine.repository.RepositoryException\nimport cats.syntax.either._\nimport com.fortysevendeg.ninecardslauncher.services.persistence.data.{\n  MomentPersistenceServicesData,\n  WidgetPersistenceServicesData\n}\nimport monix.eval.Task\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\n\ntrait MomentPersistenceServicesSpecification\n    extends Specification\n    with DisjunctionMatchers\n    with Mockito {\n\n  trait MomentPersistenceServicesScope\n      extends RepositoryServicesScope\n      with MomentTestData\n      with WidgetPersistenceServicesData\n      with MomentPersistenceServicesData {\n\n    val exception = RepositoryException(\"Irrelevant message\")\n\n  }\n\n}\n\nclass MomentPersistenceServicesImplSpec extends MomentPersistenceServicesSpecification {\n\n  \"addMoment\" should {\n\n    \"return a Moment for a valid request\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.addMoment(any) returns TaskService(Task(Either.right(repoMoment)))\n      mockWidgetRepository.addWidgets(any) returns TaskService(Task(Either.right(seqRepoWidget)))\n      val result = persistenceServices.addMoment(momentData).value.run\n\n      result must beLike {\n        case Right(moment) =>\n          moment shouldEqual moment\n      }\n    }\n\n    \"return a Moment with a empty wifi sequence for a valid request with a empty wifi sequence\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.addMoment(any) returns TaskService(\n        Task(Either.right(repoMoment.copy(data = repoMomentData.copy(wifi = \"\")))))\n      mockWidgetRepository.addWidgets(any) returns TaskService(Task(Either.right(seqRepoWidget)))\n      val result = persistenceServices.addMoment(momentData.copy(wifi = Seq.empty)).value.run\n\n      result must beLike {\n        case Right(moment) =>\n          moment shouldEqual moment\n      }\n    }\n\n    \"return a Moment with an empty timeslot sequence for a valid request with an empty timeslot\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.addMoment(any) returns TaskService(\n        Task(Either.right(repoMoment.copy(data = repoMomentData.copy(timeslot = \"[]\")))))\n      mockWidgetRepository.addWidgets(any) returns TaskService(Task(Either.right(seqRepoWidget)))\n      val result = persistenceServices.addMoment(momentData.copy(timeslot = Seq.empty)).value.run\n\n      result must beLike {\n        case Right(moment) =>\n          moment shouldEqual moment\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.addMoment(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.addMoment(momentData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"addMoments\" should {\n\n    \"return a Seq to Moment for a valid request\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.addMoments(any) returns TaskService(Task(Either.right(seqRepoMoment)))\n      mockWidgetRepository.addWidgets(any) returns TaskService(Task(Either.right(seqRepoWidget)))\n\n      val result = persistenceServices.addMoments(seqMomentData).value.run\n      result must beLike {\n        case Right(seqMoment) => seqMoment.size shouldEqual seqRepoMoment.size\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.addMoments(any) returns TaskService(Task(Either.right(seqRepoMoment)))\n      mockWidgetRepository.addWidgets(any) returns TaskService(Task(Either.left(exception)))\n\n      val result = persistenceServices.addMoments(seqMomentData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.addMoments(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.addMoments(seqMomentData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n\n  }\n\n  \"deleteAllMoments\" should {\n\n    \"return the number of elements deleted for a valid request\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.deleteMoments() returns TaskService(Task(Either.right(deletedMoments)))\n      val result = persistenceServices.deleteAllMoments().value.run\n      result shouldEqual Right(deletedMoments)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.deleteMoments() returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteAllMoments().value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteMoment\" should {\n\n    \"return the number of elements deleted for a valid request\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.deleteMoment(any) returns TaskService(Task(Either.right(deletedMoment)))\n      val result = persistenceServices.deleteMoment(moment.id).value.run\n      result shouldEqual Right(deletedMoment)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.deleteMoment(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteMoment(moment.id).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchMoments\" should {\n\n    \"return a list of Moment elements for a valid request\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.fetchMoments() returns TaskService(Task(Either.right(seqRepoMoment)))\n      val result = persistenceServices.fetchMoments.value.run\n      result must beLike {\n        case Right(momentItems) => momentItems.size shouldEqual seqMoment.size\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.fetchMoments() returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.fetchMoments.value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"findMomentById\" should {\n\n    \"return a Moment for a valid request\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.findMomentById(any) returns TaskService(\n        Task(Either.right(Option(repoMoment))))\n      val result = persistenceServices.findMomentById(momentId).value.run\n\n      result must beLike {\n        case Right(maybeMoment) =>\n          maybeMoment must beSome[Moment].which { moment =>\n            moment.id shouldEqual momentId\n          }\n      }\n    }\n\n    \"return None when a non-existent id is given\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.findMomentById(any) returns TaskService(Task(Either.right(None)))\n      val result = persistenceServices.findMomentById(nonExistentMomentId).value.run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.findMomentById(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.findMomentById(momentId).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"getMomentByCollectionId\" should {\n\n    \"return a Moment for a valid request\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.fetchMomentByCollectionId(any) returns TaskService(\n        Task(Either.right(Option(repoMoment))))\n\n      val result = persistenceServices.getMomentByCollectionId(collectionId).value.run\n\n      result must beLike {\n        case Right(maybeMoment) =>\n          maybeMoment must beSome[Moment].which { moment =>\n            moment.id shouldEqual momentId\n            moment.collectionId shouldEqual Option(collectionId)\n          }\n      }\n    }\n\n    \"return None when a non-existent id is given\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.fetchMomentByCollectionId(any) returns TaskService(\n        Task(Either.right(None)))\n      val result = persistenceServices.getMomentByCollectionId(nonExistentCollectionId).value.run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.fetchMomentByCollectionId(any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.getMomentByCollectionId(collectionId).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"getMomentByType\" should {\n\n    \"return a Moment by Type for a valid request\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.fetchMoments(any, any, any) returns TaskService(\n        Task(Either.right(seqRepoMoment)))\n\n      val result = persistenceServices.getMomentByType(momentType).value.run\n      result must beLike {\n        case Right(moment) => moment.momentType shouldEqual momentType\n      }\n    }\n\n    \"return a  PersistenceServiceException if the service return a Seq empty.\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.fetchMoments(any, any, any) returns TaskService(\n        Task(Either.right(Seq.empty)))\n\n      val result = persistenceServices.getMomentByType(momentType).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.fetchMoments(any, any, any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.getMomentByType(momentType).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n\n  }\n\n  \"fetchMomentByType\" should {\n\n    \"return a Moment by Type for a valid request\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.fetchMoments(any, any, any) returns TaskService(\n        Task(Either.right(seqRepoMoment)))\n\n      val result = persistenceServices.fetchMomentByType(momentType = momentTypeSeq(0)).value.run\n      result must beLike {\n        case Right(maybeMoment) =>\n          maybeMoment must beSome[Moment].which { moment =>\n            moment.momentType shouldEqual momentType\n          }\n      }\n    }\n\n    \"return a None if the service return a Seq empty\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.fetchMoments(any, any, any) returns TaskService(\n        Task(Either.right(Seq.empty)))\n      val result = persistenceServices.fetchMomentByType(momentType = momentTypeSeq(0)).value.run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.fetchMoments(any, any, any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.fetchMomentByType(momentType = momentTypeSeq(0)).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n\n  }\n\n  \"fetchMomentById\" should {\n\n    \"return a Moment by Id for a valid request\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.findMomentById(any) returns TaskService(\n        Task(Either.right(Option(repoMoment))))\n\n      val result = persistenceServices.fetchMomentById(moment.id).value.run\n      result must beLike {\n        case Right(maybeMoment) =>\n          maybeMoment must beSome[Moment].which { moment =>\n            moment.momentType shouldEqual momentType\n          }\n      }\n    }\n\n    \"return a None if the service return a None\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.findMomentById(any) returns TaskService(Task(Either.right(None)))\n      val result = persistenceServices.fetchMomentById(moment.id).value.run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.findMomentById(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.fetchMomentById(moment.id).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n\n  }\n\n  \"updateMoment\" should {\n\n    \"return the number of elements updated for a valid request\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.updateMoment(any) returns TaskService(Task(Either.right(deletedMoment)))\n      val result = persistenceServices.updateMoment(moment).value.run\n      result shouldEqual Right(deletedMoment)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new MomentPersistenceServicesScope {\n\n      mockMomentRepository.updateMoment(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.updateMoment(moment).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/impl/RepositoryServicesScope.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.repository.repositories._\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\ntrait RepositoryServicesScope extends Scope with Mockito {\n\n  val mockAppRepository = mock[AppRepository]\n\n  val mockCardRepository = mock[CardRepository]\n\n  val mockCollectionRepository = mock[CollectionRepository]\n\n  val mockDockAppRepository = mock[DockAppRepository]\n\n  val mockMomentRepository = mock[MomentRepository]\n\n  val mockUserRepository = mock[UserRepository]\n\n  val mockWidgetRepository = mock[WidgetRepository]\n\n  val persistenceServices = new PersistenceServicesImpl(\n    appRepository = mockAppRepository,\n    cardRepository = mockCardRepository,\n    collectionRepository = mockCollectionRepository,\n    dockAppRepository = mockDockAppRepository,\n    momentRepository = mockMomentRepository,\n    userRepository = mockUserRepository,\n    widgetRepository = mockWidgetRepository)\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/impl/UserPersistenceServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.UserTestData\nimport cards.nine.commons.test.data.UserValues._\nimport cards.nine.models.User\nimport cards.nine.repository.RepositoryException\nimport cards.nine.services.persistence.data.UserPersistenceServicesData\nimport cats.syntax.either._\nimport monix.eval.Task\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mutable.Specification\n\ntrait UserPersistenceServicesDataSpecification extends Specification with DisjunctionMatchers {\n\n  trait UserPersistenceServicesScope\n      extends RepositoryServicesScope\n      with UserTestData\n      with UserPersistenceServicesData {\n\n    val exception = RepositoryException(\"Irrelevant message\")\n\n  }\n\n}\n\nclass UserPersistenceServicesImplSpec extends UserPersistenceServicesDataSpecification {\n\n  \"addUser\" should {\n\n    \"return a User value for a valid request\" in new UserPersistenceServicesScope {\n\n      mockUserRepository.addUser(any) returns TaskService(Task(Either.right(repoUser)))\n      val result = persistenceServices.addUser(userData).value.run\n\n      result must beLike {\n        case Right(user) => user.id shouldEqual userId\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new UserPersistenceServicesScope {\n\n      mockUserRepository.addUser(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.addUser(userData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteAllUsers\" should {\n\n    \"return the number of elements deleted for a valid request\" in new UserPersistenceServicesScope {\n\n      mockUserRepository.deleteUsers() returns TaskService(Task(Either.right(deletedUsers)))\n      val result = persistenceServices.deleteAllUsers().value.run\n      result shouldEqual Right(deletedUsers)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new UserPersistenceServicesScope {\n\n      mockUserRepository.deleteUsers() returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteAllUsers().value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteUser\" should {\n\n    \"return the number of elements deleted for a valid request\" in new UserPersistenceServicesScope {\n      mockUserRepository.deleteUser(any) returns TaskService(Task(Either.right(deletedUser)))\n      val result = persistenceServices.deleteUser(user).value.run\n      result shouldEqual Right(deletedUser)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new UserPersistenceServicesScope {\n\n      mockUserRepository.deleteUser(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteUser(user).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchUsers\" should {\n\n    \"return a list of User elements for a valid request\" in new UserPersistenceServicesScope {\n\n      mockUserRepository.fetchUsers returns TaskService(Task(Either.right(seqRepoUser)))\n      val result = persistenceServices.fetchUsers.value.run\n\n      result must beLike {\n        case Right(userItems) => userItems.size shouldEqual seqUser.size\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new UserPersistenceServicesScope {\n\n      mockUserRepository.fetchUsers returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.fetchUsers.value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"findUserById\" should {\n\n    \"return a User for a valid request\" in new UserPersistenceServicesScope {\n\n      mockUserRepository.findUserById(any) returns TaskService(\n        Task(Either.right(Option(repoUser))))\n      val result = persistenceServices.findUserById(userId).value.run\n\n      result must beLike {\n        case Right(maybeUser) =>\n          maybeUser must beSome[User].which { user =>\n            user.id shouldEqual userId\n          }\n      }\n    }\n\n    \"return None when a non-existent id is given\" in new UserPersistenceServicesScope {\n\n      mockUserRepository.findUserById(any) returns TaskService(Task(Either.right(None)))\n      val result = persistenceServices.findUserById(nonExistentUserId).value.run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new UserPersistenceServicesScope {\n\n      mockUserRepository.findUserById(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.findUserById(userId).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"updateUser\" should {\n\n    \"return the number of elements updated for a valid request\" in new UserPersistenceServicesScope {\n\n      mockUserRepository.updateUser(any) returns TaskService(Task(Either.right(updatedUser)))\n      val result = persistenceServices.updateUser(user).value.run\n      result shouldEqual Right(updatedUser)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new UserPersistenceServicesScope {\n\n      mockUserRepository.updateUser(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.updateUser(user).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/persistence/impl/WidgetPersistenceServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.persistence.impl\n\nimport cards.nine.commons.services.TaskService\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.commons.test.data.WidgetTestData\nimport cards.nine.commons.test.data.WidgetValues._\nimport cards.nine.models.Widget\nimport cards.nine.repository.RepositoryException\nimport cats.syntax.either._\nimport com.fortysevendeg.ninecardslauncher.services.persistence.data.WidgetPersistenceServicesData\nimport monix.eval.Task\nimport org.specs2.matcher.DisjunctionMatchers\nimport org.specs2.mutable.Specification\n\ntrait WidgetPersistenceServicesSpecification extends Specification with DisjunctionMatchers {\n\n  trait WidgetPersistenceServicesScope\n      extends RepositoryServicesScope\n      with WidgetTestData\n      with WidgetPersistenceServicesData {\n\n    val exception = RepositoryException(\"Irrelevant message\")\n\n  }\n\n}\n\nclass WidgetPersistenceServicesImplSpec extends WidgetPersistenceServicesSpecification {\n\n  \"addWidget\" should {\n\n    \"return a Widget for a valid request\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.addWidget(any) returns TaskService(Task(Either.right(repoWidget)))\n      val result = persistenceServices.addWidget(widgetData).value.run\n\n      result must beLike {\n        case Right(widget) =>\n          widget.id shouldEqual widgetId\n          widget.packageName shouldEqual widgetPackageName\n      }\n    }\n\n    \"return Unit for a valid request when AppWidgetId is None\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.addWidget(any) returns\n        TaskService(\n          Task(Either.right(repoWidget.copy(data = repoWidgetData.copy(appWidgetId = 0)))))\n      val result = persistenceServices.addWidget(widgetData).value.run\n\n      result must beLike {\n        case Right(widget) =>\n          widget.id shouldEqual widgetId\n          widget.packageName shouldEqual widgetPackageName\n          widget.appWidgetId shouldEqual None\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new WidgetPersistenceServicesScope {\n      mockWidgetRepository.addWidget(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.addWidget(widgetData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"addWidgets\" should {\n\n    \"return the sequence of widgets added\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.addWidgets(any) returns TaskService(Task(Either.right(seqRepoWidget)))\n      val result = persistenceServices.addWidgets(seqWidgetData).value.run\n      result shouldEqual Right(seqWidget)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.addWidgets(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.addWidgets(seqWidgetData).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteAllWidgets\" should {\n\n    \"return the number of elements deleted for a valid request\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.deleteWidgets() returns TaskService(Task(Either.right(deletedWidgets)))\n      val result = persistenceServices.deleteAllWidgets().value.run\n      result shouldEqual Right(deletedWidgets)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.deleteWidgets() returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteAllWidgets().value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteWidget\" should {\n\n    \"return the number of elements deleted for a valid request\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.deleteWidget(any) returns TaskService(Task(Either.right(deletedWidget)))\n      val result = persistenceServices.deleteWidget(widget).value.run\n      result shouldEqual Right(deletedWidget)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.deleteWidget(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteWidget(widget).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"deleteWidgetsByMoment\" should {\n\n    \"return the number of elements deleted for a valid request\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.deleteWidgets(any) returns TaskService(\n        Task(Either.right(deletedWidgets)))\n      val result = persistenceServices.deleteWidgetsByMoment(widgetMomentId).value.run\n      result shouldEqual Right(deletedWidgets)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.deleteWidgets(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.deleteWidgetsByMoment(widgetMomentId).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchWidgetByAppWidgetId\" should {\n\n    \"return a Widget elements for a valid request\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.fetchWidgetByAppWidgetId(any) returns TaskService(\n        Task(Either.right(Some(repoWidget))))\n      val result = persistenceServices.fetchWidgetByAppWidgetId(appWidgetId).value.run\n      result must beLike {\n        case Right(widgets) => widgets.size shouldEqual deletedWidget\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.fetchWidgetByAppWidgetId(any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.fetchWidgetByAppWidgetId(appWidgetId).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchWidgetsByMoment\" should {\n\n    \"return a list of Widget elements for a valid request\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.fetchWidgetsByMoment(any) returns TaskService(\n        Task(Either.right(seqRepoWidget)))\n      val result = persistenceServices.fetchWidgetsByMoment(widgetMomentId).value.run\n\n      result must beLike {\n        case Right(widgets) => widgets.size shouldEqual seqWidget.size\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.fetchWidgetsByMoment(any) returns TaskService(\n        Task(Either.left(exception)))\n      val result = persistenceServices.fetchWidgetsByMoment(widgetMomentId).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"fetchWidgets\" should {\n\n    \"return a list of Widget elements for a valid request\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.fetchWidgets() returns TaskService(Task(Either.right(seqRepoWidget)))\n      val result = persistenceServices.fetchWidgets.value.run\n\n      result must beLike {\n        case Right(widgets) => widgets.size shouldEqual seqWidget.size\n      }\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.fetchWidgets() returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.fetchWidgets.value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"findWidgetById\" should {\n\n    \"return a Widget for a valid request\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.findWidgetById(any) returns TaskService(\n        Task(Either.right(Option(repoWidget))))\n      val result = persistenceServices.findWidgetById(widgetId).value.run\n\n      result must beLike {\n        case Right(maybeWidget) =>\n          maybeWidget must beSome[Widget].which { widget =>\n            widget.id shouldEqual widgetId\n          }\n      }\n    }\n\n    \"return None when a non-existent id is given\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.findWidgetById(any) returns TaskService(Task(Either.right(None)))\n      val result = persistenceServices.findWidgetById(nonExistentWidgetId).value.run\n      result shouldEqual Right(None)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.findWidgetById(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.findWidgetById(widgetId).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"updateWidget\" should {\n\n    \"return the number of elements updated for a valid request\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.updateWidget(any) returns TaskService(Task(Either.right(deletedWidget)))\n      val result = persistenceServices.updateWidget(widget).value.run\n      result shouldEqual Right(deletedWidget)\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.updateWidget(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.updateWidget(widget).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n\n  \"updateWidgets\" should {\n\n    \"return the sequence with the number of elements updated for a valid request\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.updateWidgets(any) returns TaskService(\n        Task(Either.right(Seq(updatedWidgets))))\n      val result = persistenceServices.updateWidgets(seqWidget).value.run\n      result shouldEqual Right(Seq(updatedWidgets))\n    }\n\n    \"return a PersistenceServiceException if the service throws a exception\" in new WidgetPersistenceServicesScope {\n\n      mockWidgetRepository.updateWidgets(any) returns TaskService(Task(Either.left(exception)))\n      val result = persistenceServices.updateWidgets(seqWidget).value.run\n      result must beAnInstanceOf[Left[RepositoryException, _]]\n    }\n  }\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/shortcuts/impl/ShortCutsServicesImplData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.shortcuts.impl\n\nimport android.content.Intent\nimport cards.nine.models.Shortcut\n\ntrait ShortcutsServicesImplData {\n\n  val name        = \"Name\"\n  val packageName = \"com.fortysevendeg.ninecardslauncher.test.sampleapp\"\n\n  val intent = new Intent(Intent.ACTION_CREATE_SHORTCUT)\n\n  val sampleShortcut1 = Shortcut(title = \"B - Sample Name 1\", icon = None, intent = intent)\n\n  val sampleShortcut2 = Shortcut(title = \"A - Sample Name 2\", icon = None, intent = intent)\n\n  val shotcutsList = Seq(sampleShortcut1, sampleShortcut2)\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/shortcuts/impl/ShortCutsServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.shortcuts.impl\n\nimport android.content.ComponentName\nimport android.content.pm.{ActivityInfo, ApplicationInfo, PackageManager, ResolveInfo}\nimport android.graphics.drawable.Drawable\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.test.TaskServiceSpecification\nimport cards.nine.models.Shortcut\nimport cards.nine.services.shortcuts.ShortcutServicesException\nimport org.specs2.mock.Mockito\nimport org.specs2.specification.Scope\n\nimport scala.collection.JavaConversions._\n\ntrait ShortcutsImplSpecification extends TaskServiceSpecification with Mockito {\n\n  trait ShortcutsImplScope extends Scope with ShortcutsServicesImplData {\n\n    val packageManager = mock[PackageManager]\n    val contextSupport = mock[ContextSupport]\n    val mockIcon       = mock[Drawable]\n    contextSupport.getPackageManager returns packageManager\n\n    def createMockResolveInfo(sampleShortcut: Shortcut): ResolveInfo = {\n      val sampleResolveInfo   = mock[ResolveInfo]\n      val mockActivityInfo    = mock[ActivityInfo]\n      val mockApplicationInfo = mock[ApplicationInfo]\n      sampleResolveInfo.loadLabel(packageManager) returns sampleShortcut.title\n\n      mockApplicationInfo.packageName = packageName\n      mockActivityInfo.applicationInfo = mockApplicationInfo\n      mockActivityInfo.name = name\n      packageManager.getActivityIcon(any[ComponentName]) returns mockIcon\n      sampleResolveInfo.activityInfo = mockActivityInfo\n      sampleResolveInfo\n    }\n\n    val mockShortcuts =\n      List(createMockResolveInfo(sampleShortcut1), createMockResolveInfo(sampleShortcut2))\n\n    val shortcutsServicesImpl = new ShortcutsServicesImpl\n  }\n}\n\nclass ShortcutsServicesImplSpec extends ShortcutsImplSpecification {\n\n  \"returns the ordered list of shortcuts when they exist\" in\n    new ShortcutsImplScope { //TODO  we need to improve this tests - issue #907\n\n      packageManager.queryIntentActivities(any, any) returns mockShortcuts\n\n      shortcutsServicesImpl.getShortcuts(contextSupport).mustRight { result =>\n        result.size shouldEqual shotcutsList.size\n      }\n    }\n\n  \"returns an ShortcutException when no shortcuts exist\" in\n    new ShortcutsImplScope {\n\n      val exception = ShortcutServicesException(\"\")\n      packageManager.queryIntentActivities(any, any) throws exception\n\n      shortcutsServicesImpl.getShortcuts(contextSupport).mustLeft[ShortcutServicesException]\n    }\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/utils/ResourceUtilsData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.utils\n\ntrait ResourceUtilsData {\n\n  val fileFolder = \"/file/example\"\n\n  val packageName = \"com.fortysevendeg.ninecardslauncher.test\"\n\n  val className = \"ClassNameExample\"\n\n  val fileName = String.format(\n    \"%s_%s\",\n    packageName.toLowerCase.replace(\".\", \"_\"),\n    className.toLowerCase.replace(\".\", \"_\"))\n\n  val resultFilePath = s\"$fileFolder/$fileName\"\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/utils/ResourceUtilsSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.utils\n\nimport java.io.File\n\nimport cards.nine.commons.contexts.ContextSupport\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait ResourceUtilsSpecification extends Specification with Mockito {\n\n  trait ResourceUtilsScope extends Scope with ResourceUtilsData {\n\n    val mockContextSupport = mock[ContextSupport]\n    val resourceUtils      = new ResourceUtils\n    val mockFile           = mock[File]\n  }\n}\n\nclass ResourceUtilsSpec extends ResourceUtilsSpecification {\n\n  \"Resource Utils\" should {\n\n    \"return the file path when a valid file name is provided\" in\n      new ResourceUtilsScope {\n\n        mockContextSupport.getAppIconsDir returns mockFile\n        mockFile.getPath returns fileFolder\n\n        val result = resourceUtils.getPath(fileName)(mockContextSupport)\n        result shouldEqual resultFilePath\n      }\n  }\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/widgets/impl/WidgetsServicesImplData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.widgets.impl\n\nimport cards.nine.models.AppWidget\nimport cards.nine.models.types.WidgetResizeMode\n\nimport scala.util.Random\n\ntrait WidgetsServicesImplData {\n\n  val userHashCode: Int       = Random.nextInt(10)\n  val autoAdvanceViewId: Int  = Random.nextInt(10)\n  val initialLayout: Int      = Random.nextInt(10)\n  val minHeight: Int          = Random.nextInt(10)\n  val minResizeHeight: Int    = Random.nextInt(10)\n  val minResizeWidth: Int     = Random.nextInt(10)\n  val minWidth: Int           = Random.nextInt(10)\n  val className: String       = Random.nextString(5)\n  val packageName: String     = Random.nextString(5)\n  val resizeMode: Int         = Random.nextInt(10)\n  val updatePeriodMillis: Int = Random.nextInt(10)\n  val label: String           = Random.nextString(5)\n  val preview: Int            = Random.nextInt(10)\n\n  val userHashCodeOption = Option(userHashCode)\n  val previewOption      = Option(preview)\n\n  def createSeqWidget(\n      num: Int = 5,\n      userHashCode: Option[Int] = userHashCodeOption,\n      autoAdvanceViewId: Int = autoAdvanceViewId,\n      initialLayout: Int = initialLayout,\n      minHeight: Int = minHeight,\n      minResizeHeight: Int = minResizeHeight,\n      minResizeWidth: Int = minResizeWidth,\n      minWidth: Int = minWidth,\n      className: String = className,\n      packageName: String = packageName,\n      resizeMode: Int = resizeMode,\n      updatePeriodMillis: Int = updatePeriodMillis,\n      label: String = label,\n      preview: Int = preview): Seq[AppWidget] =\n    List.tabulate(num)(\n      item =>\n        AppWidget(\n          userHashCode = userHashCode,\n          autoAdvanceViewId = autoAdvanceViewId,\n          initialLayout = initialLayout,\n          minHeight = minHeight,\n          minResizeHeight = minResizeHeight,\n          minResizeWidth = minResizeHeight,\n          minWidth = minWidth,\n          className = className,\n          packageName = packageName,\n          resizeMode = WidgetResizeMode(resizeMode),\n          updatePeriodMillis = updatePeriodMillis,\n          label = label,\n          preview = preview))\n\n  val seqWidget: Seq[AppWidget] = createSeqWidget()\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/widgets/impl/WidgetsServicesImplSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.widgets.impl\n\nimport android.content.pm.PackageManager\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.commons.test.TaskServiceTestOps._\nimport cards.nine.models.Conversions\nimport cards.nine.services.widgets.WidgetServicesException\nimport cards.nine.services.widgets.utils.AppWidgetManagerCompat\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait WidgetsImplSpecification extends Specification with Mockito {\n\n  trait WidgetsImplScope extends Scope with WidgetsServicesImplData {\n\n    val mockContextSupport   = mock[ContextSupport]\n    val mockPackageManager   = mock[PackageManager]\n    val mockAppWidgetManager = mock[AppWidgetManagerCompat with Conversions]\n\n    val widgetsServicesImpl = new WidgetsServicesImpl {\n      override protected def getAppWidgetManager(implicit context: ContextSupport) =\n        mockAppWidgetManager\n    }\n\n    val exception = WidgetServicesException(\"\")\n  }\n\n}\n\nclass WidgetsServicesImplSpec extends WidgetsImplSpecification {\n\n  \"returns the list of widgets\" in\n    new WidgetsImplScope {\n\n      mockContextSupport.getPackageManager returns mockPackageManager\n      mockAppWidgetManager.getAllProviders returns seqWidget\n\n      val result = widgetsServicesImpl.getWidgets(mockContextSupport).value.run\n      result shouldEqual Right(seqWidget)\n    }\n\n  \"returns an WidgetException when no widgets exist\" in\n    new WidgetsImplScope {\n\n      mockAppWidgetManager.getAllProviders throws exception\n      val result = widgetsServicesImpl.getWidgets(mockContextSupport).value.run\n      result must beAnInstanceOf[Left[WidgetServicesException, _]]\n    }\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/widgets/impl/utils/impl/AppWidgetManagerData.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.widgets.impl.utils.impl\n\nimport cards.nine.models.AppWidget\nimport cards.nine.models.types.WidgetResizeMode\n\nimport scala.util.Random\n\ntrait AppWidgetManagerData {\n\n  val userHashCode: Int       = Random.nextInt(10)\n  val autoAdvanceViewId: Int  = Random.nextInt(10)\n  val initialLayout: Int      = Random.nextInt(10)\n  val minHeight: Int          = Random.nextInt(10)\n  val minResizeHeight: Int    = Random.nextInt(10)\n  val minResizeWidth: Int     = Random.nextInt(10)\n  val minWidth: Int           = Random.nextInt(10)\n  val className: String       = Random.nextString(5)\n  val packageName: String     = Random.nextString(5)\n  val resizeMode: Int         = Random.nextInt(10)\n  val updatePeriodMillis: Int = Random.nextInt(10)\n  val label: String           = Random.nextString(5)\n  val preview: Int            = Random.nextInt(10)\n\n  val userHashCodeOption = Option(userHashCode)\n\n  def createSeqWidget(\n      num: Int = 5,\n      userHashCode: Option[Int] = userHashCodeOption,\n      autoAdvanceViewId: Int = autoAdvanceViewId,\n      initialLayout: Int = initialLayout,\n      minHeight: Int = minHeight,\n      minResizeHeight: Int = minResizeHeight,\n      minResizeWidth: Int = minResizeWidth,\n      minWidth: Int = minWidth,\n      className: String = className,\n      packageName: String = packageName,\n      resizeMode: Int = resizeMode,\n      updatePeriodMillis: Int = updatePeriodMillis,\n      label: String = label,\n      preview: Int = preview): Seq[AppWidget] =\n    List.tabulate(num)(\n      item =>\n        AppWidget(\n          userHashCode = userHashCode,\n          autoAdvanceViewId = autoAdvanceViewId,\n          initialLayout = initialLayout,\n          minHeight = minHeight,\n          minResizeHeight = minResizeHeight,\n          minResizeWidth = minResizeWidth,\n          minWidth = minWidth,\n          className = className,\n          packageName = packageName,\n          resizeMode = WidgetResizeMode(resizeMode),\n          updatePeriodMillis = updatePeriodMillis,\n          label = label,\n          preview = preview))\n\n  val seqWidget: Seq[AppWidget] = createSeqWidget()\n\n}\n"
  },
  {
    "path": "modules/services/src/test/scala/cards/nine/services/widgets/impl/utils/impl/AppWidgetManagerImplDefaultSpec.scala",
    "content": "/*\n * Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cards.nine.services.widgets.impl.utils.impl\n\nimport android.appwidget.AppWidgetProviderInfo\nimport android.content.ComponentName\nimport android.content.pm.PackageManager\nimport cards.nine.commons.contexts.ContextSupport\nimport cards.nine.services.widgets.utils.impl.AppWidgetManagerImplDefault\nimport org.specs2.mock.Mockito\nimport org.specs2.mutable.Specification\nimport org.specs2.specification.Scope\n\ntrait AppWidgetManagerDefaultImplSpecification extends Specification with Mockito {\n\n  trait AppWidgetManagerDefaultImplScope extends Scope with AppWidgetManagerData {\n\n    implicit val mockContextSupport = mock[ContextSupport]\n    val mockPackageManager          = mock[PackageManager]\n\n    mockContextSupport.getPackageManager returns mockPackageManager\n\n    def createComponentName: ComponentName = {\n      val sampleComponentName = mock[ComponentName]\n      sampleComponentName.getClassName returns className\n      sampleComponentName.getPackageName returns packageName\n      sampleComponentName\n    }\n\n    def createAppWidgetProviderInfo: AppWidgetProviderInfo = {\n      val sampleAppWidgetProviderInfo = mock[AppWidgetProviderInfo]\n      sampleAppWidgetProviderInfo.autoAdvanceViewId = autoAdvanceViewId\n      sampleAppWidgetProviderInfo.initialLayout = initialLayout\n      sampleAppWidgetProviderInfo.minHeight = minHeight\n      sampleAppWidgetProviderInfo.minResizeHeight = minResizeHeight\n      sampleAppWidgetProviderInfo.minResizeWidth = minResizeWidth\n      sampleAppWidgetProviderInfo.minWidth = minWidth\n      sampleAppWidgetProviderInfo.resizeMode = resizeMode\n      sampleAppWidgetProviderInfo.updatePeriodMillis = updatePeriodMillis\n      sampleAppWidgetProviderInfo.previewImage = preview\n      sampleAppWidgetProviderInfo.provider = createComponentName\n      sampleAppWidgetProviderInfo\n    }\n\n    def createSeqAppWidgetProviderInfo(\n        num: Int = 5,\n        appWidgetProviderInfo: AppWidgetProviderInfo = createAppWidgetProviderInfo\n    ): Seq[AppWidgetProviderInfo] = List.tabulate(num)(item => appWidgetProviderInfo)\n\n    val seqAppWidgetProviderInfo: Seq[AppWidgetProviderInfo] = createSeqAppWidgetProviderInfo()\n\n    val appWidgetManagerImplDefault = new AppWidgetManagerImplDefault {\n\n      override protected def getAppWidgetProviderInfo: Seq[AppWidgetProviderInfo] =\n        seqAppWidgetProviderInfo\n\n      override protected def getLabel(info: AppWidgetProviderInfo): String = label\n\n      override protected def getUser(info: AppWidgetProviderInfo): Option[Int] = userHashCodeOption\n    }\n  }\n\n}\n\nclass AppWidgetManagerDefaultImplSpec extends AppWidgetManagerDefaultImplSpecification {\n\n  \"Get All Providers\" should {\n\n    \"returns the list of widgets\" in\n      new AppWidgetManagerDefaultImplScope {\n\n        val result = appWidgetManagerImplDefault.getAllProviders\n        result shouldEqual seqWidget\n      }\n\n    \"returns an empty list when no AppWidgetProviderInfo is found\" in\n      new AppWidgetManagerDefaultImplScope {\n\n        override val appWidgetManagerImplDefault = new AppWidgetManagerImplDefault {\n          override protected def getAppWidgetProviderInfo: Seq[AppWidgetProviderInfo] = Nil\n        }\n\n        val result = appWidgetManagerImplDefault.getAllProviders\n        result shouldEqual Nil\n      }\n  }\n\n}\n"
  },
  {
    "path": "ninecards.properties.default",
    "content": "# Backend V2\nbackend.v2.url=https://nine-cards-stage.herokuapp.com\nbackend.v2.clientid=\n\n# Third Parties\ncrashlytics.enabled=false\ncrashlytics.apikey=\ncrashlytics.apisecret=\nstrictmode.enabled=false\nanalytics.enabled=false\nanalytics.trackid=\n\n# Firebase\nfirebase.enabled=false\nfirebase.url=\nfirebase.google.appid=\nfirebase.google.apikey=\nfirebase.gcm.senderid=\nfirebase.clientid=\n\n# FlowUp\nflowup.enabled=false\nflowup.apikey=\n\n# Apptentive\napptentive.enabled=\napptentive.apikey="
  },
  {
    "path": "project/AppBuild.scala",
    "content": "import ReplacePropertiesGenerator._\nimport Settings._\nimport Versions._\nimport android.AndroidApp\nimport android.Keys._\nimport sbt.Keys._\nimport sbt._\nimport microsites.MicrositesPlugin\n\nobject AppBuild extends Build {\n\n  def excludeArtifact(module: ModuleID, artifactOrganizations: String*): ModuleID =\n    module.excludeAll(artifactOrganizations map (org => ExclusionRule(organization = org)): _*)\n\n  lazy val root = Project(id = \"root\", base = file(\".\"))\n      .settings(\n        scalaVersion := scalaV,\n        name := \"9 Cards 2.0\",\n        scalacOptions ++= Seq(\"-feature\", \"-deprecation\"),\n        platformTarget in Android := androidPlatformV,\n        packageRelease <<= packageRelease in Android in app,\n        packageDebug <<= packageDebug in Android in app,\n        install <<= install in Android in app,\n        run <<= (run in Android in app),\n        applicationId in Android := \"com.fortysevendeg.ninecardslauncher\"\n      )\n      .aggregate(app)\n\n  lazy val app = Project(id = \"app\", base = file(\"modules/app\"))\n    .enablePlugins(AndroidApp)\n    .dependsOn(process, commonsTests % \"test->test\")\n    .settings(projectDependencies ~= (_.map(excludeArtifact(_, \"com.android\"))))\n    .settings(\n      outputLayout in Android <<= (outputLayout in Android),\n      packageResources in Android <<= (packageResources in Android).dependsOn(replaceValuesTask)\n    )\n    .settings(appSettings: _*)\n\n  lazy val process = Project(id = \"process\", base = file(\"modules/process\"))\n    .settings(processSettings: _*)\n    .enablePlugins(AndroidApp)\n    .dependsOn(services, commonsTests % \"test->test\")\n\n  lazy val services = Project(id = \"services\", base = file(\"modules/services\"))\n    .settings(servicesSettings: _*)\n    .dependsOn(api, repository, models, commonsTests % \"test->test\")\n\n  lazy val api = Project(id = \"api\", base = file(\"modules/api\"))\n    .settings(apiSettings: _*)\n    .dependsOn(commons, models, commonsTests % \"test->test\")\n\n  lazy val repository = Project(id = \"repository\", base = file(\"modules/repository\"))\n    .settings(repositorySettings: _*)\n    .dependsOn(commons, models, commonsTests % \"test->test\")\n\n  lazy val models = Project(id = \"models\", base = file(\"modules/models\"))\n    .settings(modelsSettings: _*)\n    .dependsOn(commons, mockAndroid % \"test->test\")\n\n  lazy val commons = Project(id = \"commons\", base = file(\"modules/commons\"))\n    .settings(commonsSettings: _*)\n    .dependsOn(mockAndroid % \"test->test\")\n\n  lazy val commonsTests = Project(id = \"commons-tests\", base = file(\"modules/commons-tests\"))\n    .settings(commonsTestsSettings: _*)\n    .dependsOn(commons, mockAndroid, models)\n\n  lazy val mockAndroid = Project(id = \"mockAndroid\", base = file(\"modules/mock-android\"))\n    .settings(mockAndroidSettings: _*)\n\n  lazy val tests = Project(id = \"tests\", base = file(\"modules/tests\"))\n    .settings(commonsSettings: _*)\n    .aggregate(process, services, api, repository, commons, commonsTests)\n\n  lazy val docs = (project in file(\"modules/docs\"))\n    .settings(commonsSettings: _*)\n    .settings(micrositeSettings: _*)\n    .settings(moduleName := \"docs\")\n    .enablePlugins(MicrositesPlugin)\n    .settings(\n      name := \"docs\",\n      description := \"9Cards Documentation\")\n\n}\n"
  },
  {
    "path": "project/Crashlytics.scala",
    "content": "import java.io.File\n\nimport ReplacePropertiesGenerator._\nimport android.Keys._\nimport sbt.Keys._\nimport sbt._\n\nobject Crashlytics {\n\n  lazy val crashlyticsSettings =\n      Seq(\n        collectResources in Android <<= (collectResources in Android)\n          dependsOn fixNameSpace\n          dependsOn crashlyticsPreBuild\n          dependsOn createFiles,\n        zipalign in Android <<= (zipalign in Android) map { result =>\n          crashlyticsPostPackage\n          result\n        }\n      )\n\n  val requiredProperties = Seq(\"crashlytics.apikey\", \"crashlytics.apisecret\")\n\n  def createFiles = Def.task[Seq[File]] {\n    val log = streams.value.log\n    configTask[Seq[File]](log, Seq.empty) {\n      log.info(\"Creating crashlytics files\")\n      try {\n        val templates = loadTemplates(baseDirectory.value / \"crashlytics\" / \"templates\")\n        templates map { file =>\n          val target = baseDirectory.value / \"crashlytics\" / file.getName\n          replaceContent(file, target)(log)\n          target\n        }\n      } catch {\n        case e: Throwable =>\n          log.error(\"An error occurred loading creating files\")\n          throw e\n      }\n    }\n  }\n\n  /*\n   * Removes the namespace from the crashlytics auto-generated file.\n   * This is a common problem of the com.android.tools.build:builder and crashlytics\n   * that can be solved by simply removing the namespace declaration and the attributes\n   * with namespace\n   */\n  def fixNameSpace = Def.task[Unit] {\n    val log = streams.value.log\n    configTask[Unit](log, ()) {\n      crashlyticsCodeGen.value\n      val file = baseDirectory.value / \"src/main/res/values/com_crashlytics_export_strings.xml\"\n      if (file.exists()) {\n        val xml = scala.xml.XML.loadFile(file)\n\n        val rewriteRule = new scala.xml.transform.RewriteRule {\n          override def transform(node: scala.xml.Node) = node match {\n            case elem: scala.xml.Elem if elem.label == \"resources\" =>\n              elem.copy(child = fixChilds(elem.child))\n            case x => x\n          }\n        }\n\n        scala.xml.XML.save(\n          filename = file.getAbsolutePath,\n          node = rewriteRule(xml),\n          enc = \"UTF-8\",\n          xmlDecl = true)\n      }\n    }\n  }\n\n  def crashlyticsPreBuild = Def.task[Unit] {\n    val log = streams.value.log\n    configTask[Unit](log, ()) {\n      log.info(\"Crashlytics pre build\")\n\n      // Cleanup\n      crashlyticsCleanupResources.value\n\n      // Upload deobs - Disabled\n      // crashlyticsUploadDeobs.value\n    }\n  }\n\n  def crashlyticsCodeGen = Def.task[Unit] {\n    val log = streams.value.log\n    configTask[Unit](log, ()) {\n      log.info(\"Crashlytics code gen\")\n\n      // Generate resources\n      crashlyticsGenerateResources.value\n    }\n  }\n\n  def crashlyticsPostPackage = Def.task[Unit] {\n    val log = streams.value.log\n    configTask[Unit](log, ()) {\n      log.info(\"Crashlytics post package\")\n\n      // Store deobs - Disabled\n      // crashlyticsStoreDeobs.value\n\n      // Upload deobs - Disabled\n      // crashlyticsUploadDeobs.value\n\n      // Cleanup\n      crashlyticsCleanupResources.value\n    }\n  }\n\n  def crashlyticsCleanupResources = Def.task[Unit] {\n    val log = streams.value.log\n    configTask[Unit](log, ()) {\n      crashlyticsTask(\n        log = streams.value.log,\n        task = Crashlytics.CleanupResources,\n        projectPath = baseDirectory.value.getAbsolutePath)\n    }\n  }\n\n  def crashlyticsGenerateResources = Def.task[Unit] {\n    val log = streams.value.log\n    configTask[Unit](log, ()) {\n      crashlyticsTask(\n        log = streams.value.log,\n        task = Crashlytics.GenerateResources,\n        projectPath = baseDirectory.value.getAbsolutePath,\n        extraArgs = Seq(\n          \"-buildEvent\",\n          \"-tool\", \"com.crashlytics.tools.ant\",\n          \"-version\", \"1.20.0\"))\n    }\n  }\n\n  def crashlyticsStoreDeobs = Def.task[Unit] {\n    val log = streams.value.log\n    configTask[Unit](log, ()) {\n      crashlyticsTask(\n        log = streams.value.log,\n        task = Crashlytics.StoreDeobs,\n        projectPath = baseDirectory.value.getAbsolutePath,\n        extraArgs = Seq(\n          s\"${(baseDirectory.value / \"proguard-mapping.txt\").getAbsolutePath}\",\n          \"-obfuscating\",\n          \"-obfuscator\",\n          \"proguard\",\n          \"-obVer\",\n          \"4.7\",\n          \"-verbose\"))\n    }\n  }\n\n  def crashlyticsUploadDeobs = Def.task[Unit] {\n    val log = streams.value.log\n    configTask[Unit](log, ()) {\n      crashlyticsTask(\n        log = streams.value.log,\n        task = Crashlytics.UploadDeobs,\n        projectPath = baseDirectory.value.getAbsolutePath,\n        extraArgs = Seq(\"-verbose\"))\n    }\n  }\n\n  object Crashlytics {\n\n    sealed trait Task {\n      def param: String\n    }\n\n    case object CleanupResources extends Task {\n      val param = \"-cleanupResourceFile\"\n    }\n\n    case object GenerateResources extends Task {\n      val param = \"-generateResourceFile\"\n    }\n\n    case object StoreDeobs extends Task {\n      val param = \"-storeDeobs\"\n    }\n\n    case object UploadDeobs extends Task {\n      val param = \"-uploadDeobs\"\n    }\n\n  }\n\n  private[this] def configTask[T](log: Logger, defaultValue: T)(f: => T) = {\n\n    val enabled = requiredProperties.foldLeft(true) {\n      case (false, _) => false\n      case (true, prop) => propertiesMap.get(prop).exists(_.nonEmpty)\n    }\n\n    if (enabled) f else {\n      log.info(\"Skipping crashlytics: There are some missing required properties\")\n      defaultValue\n    }\n\n  }\n\n  private[this] def crashlyticsTask(\n    log: Logger,\n    task: Crashlytics.Task,\n    projectPath: String,\n    extraArgs: Seq[String] = Seq.empty) = {\n\n    log.info(s\"Crashlytics task: ${task.toString}\")\n\n    val args = Seq(\n      \"-projectPath\", projectPath,\n      \"-androidManifest\", s\"$projectPath/crashlytics/CrashlyticsManifest.xml\",\n      \"-androidRes\", s\"$projectPath/src/main/res\",\n      \"-androidAssets\", s\"$projectPath/src/main/assets\",\n      task.param,\n      \"-properties\", s\"$projectPath/crashlytics/fabric.properties\") ++ extraArgs\n\n    log.debug(s\"Arguments: $args\")\n    try {\n      com.crashlytics.tools.android.DeveloperTools.main(args.toArray)\n    } catch {\n      case e: Throwable =>\n        log.error(s\"Error executing crashlytics task: ${e.getMessage}\")\n        throw e\n    }\n  }\n\n  private[this] def loadTemplates(folder: File): Seq[File] = {\n    folder.listFiles().toSeq\n  }\n\n  private[this] def fixChilds(child: Seq[scala.xml.Node]): Seq[scala.xml.Node] = {\n    child map {\n      case elem: scala.xml.Elem =>\n        elem.copy(\n          scope = scala.xml.TopScope,\n          attributes = elem.attributes.filter { a =>\n            Option(a.getNamespace(elem)).isEmpty\n          })\n      case x => x\n    }\n  }\n\n}"
  },
  {
    "path": "project/Libraries.scala",
    "content": "import sbt._\nimport Versions._\n\nobject Libraries {\n\n  def onCompile(dep: ModuleID): ModuleID = dep % \"compile\"\n  def onTest(dep: ModuleID): ModuleID = dep % \"test\"\n\n  object scala {\n\n    lazy val scalaReflect = \"org.scala-lang\" % \"scala-reflect\" % scalaV\n    lazy val scalap = \"org.scala-lang\" % \"scalap\" % scalaV\n  }\n\n  object cats {\n    lazy val cats = \"org.typelevel\" %% \"cats\" % Versions.catsV\n  }\n\n  object monix {\n    lazy val monixTypes = \"io.monix\" %% \"monix-types\" % Versions.monixV\n    lazy val monixEval = \"io.monix\" %% \"monix-eval\" % Versions.monixV\n    lazy val monixCats = \"io.monix\" %% \"monix-cats\" % Versions.monixV\n  }\n\n  object android {\n\n    def androidDep(module: String) = \"com.android.support\" % module % androidV\n\n    lazy val multiDexLib = \"com.android.support\" % \"multidex\" % multiDexV\n\n    lazy val androidProvidedLib = \"com.google.android\" % \"android\" % androidProvidedV % \"provided\"\n\n    lazy val androidSupportv4 = androidDep(\"support-v4\")\n    lazy val androidAppCompat = androidDep(\"appcompat-v7\")\n    lazy val androidRecyclerview = androidDep(\"recyclerview-v7\")\n    lazy val androidCardView = androidDep(\"cardview-v7\")\n    lazy val androidDesign = androidDep(\"design\")\n    lazy val androidFlexbox = \"com.google.android\" % \"flexbox\" % flexboxV\n  }\n\n  object macroid {\n\n    def macroid(module: String = \"\") =\n      \"org.macroid\" %% s\"macroid${if (!module.isEmpty) s\"-$module\" else \"\"}\" % macroidV\n\n    lazy val macroidRoot = macroid()\n    lazy val macroidExtras = macroid(\"extras\")\n  }\n\n  object json {\n    lazy val playJson = \"com.typesafe.play\" %% \"play-json\" % playJsonV\n  }\n\n  object net {\n    lazy val okHttp = \"com.squareup.okhttp3\" % \"okhttp\" % okHttpV\n  }\n\n  object date {\n    lazy val prettyTime = \"org.ocpsoft.prettytime\" % \"prettytime\" % prettyTimeV\n    lazy val jodaTime = \"joda-time\" % \"joda-time\" % jodaTimeV\n    lazy val gfcTimeUUID = \"com.gilt\" %% \"gfc-timeuuid\" % gfcTimeUUIDV\n  }\n\n  object test {\n\n    lazy val specs2Lib = \"org.specs2\" %% \"specs2-core\" % specs2V\n    lazy val specs2 = specs2Lib % \"test\"\n\n    lazy val mockitoLib = \"org.specs2\" % \"specs2-mock_2.11\" % mockitoV\n    lazy val mockito = mockitoLib % \"test\"\n\n    lazy val androidTest = \"com.google.android\" % \"android\" % \"4.1.1.4\" % \"test\"\n  }\n\n  object graphics {\n    lazy val glide = \"com.github.bumptech.glide\" % \"glide\" % glideV\n  }\n\n  object google {\n\n    def playServicesDep(module: String) = \"com.google.android.gms\" % module % playServicesV\n\n    // Google Actions, Google Analytics and Google Cloud Messaging\n    lazy val playServicesBase = playServicesDep(\"play-services-base\")\n    lazy val playServicesDrive = playServicesDep(\"play-services-drive\")\n    lazy val playServicesAnalytics = playServicesDep(\"play-services-analytics\")\n    lazy val playServicesAuth = playServicesDep(\"play-services-auth\")\n    lazy val playServicesPlus = playServicesDep(\"play-services-plus\")\n    lazy val playServicesAwareness = playServicesDep(\"play-services-awareness\")\n\n    lazy val firebaseCore = \"com.google.firebase\" % \"firebase-core\" % playServicesV\n    lazy val firebaseMessaging = \"com.google.firebase\" % \"firebase-messaging\" % playServicesV\n  }\n\n  object debug {\n    lazy val stetho = \"com.facebook.stetho\" % \"stetho\" % stethoV\n    lazy val stethoOkhttp = \"com.facebook.stetho\" % \"stetho-okhttp3\" % stethoV\n    lazy val stethoUrlconnection = \"com.facebook.stetho\" % \"stetho-urlconnection\" % stethoV\n    lazy val crashlytics = \"com.crashlytics.sdk.android\" % \"crashlytics\" % crashlyticsV\n    lazy val apptentive = \"com.apptentive\" % \"apptentive-android\" % apptentiveV\n  }\n\n  object performance {\n    lazy val flowUp = \"io.flowup\" % \"android-sdk\" % flowUpV\n  }\n\n}\n"
  },
  {
    "path": "project/Proguard.scala",
    "content": "import Proguard.Packages._\n\nobject Proguard {\n\n  lazy val proguardCommons = Seq(\n    \"-ignorewarnings\",\n    \"-keepattributes Signature\",\n    \"-keepattributes InnerClasses\",\n    \"-dontwarn scala.collection.**\",\n    \"-dontobfuscate\",\n    \"-keep class org.ocpsoft.prettytime.i18n.**\",\n    \"-keep class android.support.v7.widget.SearchView { <init>(...); }\",\n    \"-keep class android.support.v7.internal.widget.* { <init>(...); }\",\n    \"-keep class scala.Dynamic\",\n    \"-keep class macroid.** { *; }\",\n    \"-keep class com.fortysevendeg.** { *; }\",\n    \"-keep class com.facebook.stetho.** { *; }\",\n    \"-keep class com.crashlytics.** { *; }\",\n    \"-dontwarn com.crashlytics.**\",\n    \"-keep class io.realm.annotations.RealmModule\",\n    \"-keep @io.realm.annotations.RealmModule class *\",\n    \"-keep class io.realm.internal.Keep\",\n    \"-keep @io.realm.internal.Keep class *\",\n    \"-dontwarn javax.**\",\n    \"-dontwarn io.realm.**\")\n\n  lazy val proguardCacheList =\n    pgApplication ::\n      pgMacroid ::\n      pgMacroidExtras ::\n      pgAndroidSupport ::\n      pgPlayServices ::\n//      pgFabric ::\n//      pgCrashlytcs ::\n//      pgStetho ::\n      pgGlide ::\n      pgOkHttp ::\n      pgOkio ::\n      pgJackson ::\n      pgTypesafeConfig ::\n      pgJavax ::\n      pgApacheCommons ::\n      pgJodaTime ::\n      pgPlayApi ::\n      pgWartRemover ::\n      pgScalaz ::\n      pgScala ::\n      Nil\n\n  object Packages {\n\n    val pgApplication = \"cards.nine\"\n    val pgMacroid = \"macroid\"\n    val pgMacroidExtras = \"com.fortysevendeg.macroid.extras\"\n\n    val pgAndroidSupport = \"android.support\"\n    val pgPlayServices = \"com.google.android.gms\"\n\n    val pgFabric = \"io.fabric.sdk\"\n    val pgCrashlytcs = \"com.crashlytics.android\"\n    val pgStetho = \"com.facebook.stetho\"\n\n    val pgGlide = \"com.bumptech.glide\"\n    val pgOkHttp = \"com.skuareup.okhttp\"\n    val pgOkio = \"okio\"\n\n    val pgJackson = \"com.fasterxml.jackson\"\n    val pgTypesafeConfig = \"com.typesafe.config\"\n    val pgJavax = \"javax.annotation\"\n    val pgApacheCommons = \"org.apache.commons\"\n    val pgJodaTime = \"org.joda\"\n    val pgPlayApi = \"play.api\"\n    val pgWartRemover =\"org.brianmckenna.wartremover\"\n\n    val pgScalaz = \"scalaz\"\n\n    val pgScala = \"scala\"\n\n  }\n\n}"
  },
  {
    "path": "project/ReplacePropertiesGenerator.scala",
    "content": "import java.io.{File, FileInputStream}\nimport java.util.Properties\n\nimport android.Keys._\nimport sbt.Keys._\nimport sbt._\n\nimport scala.annotation.tailrec\nimport scala.collection.JavaConverters._\n\nobject ReplacePropertiesGenerator {\n\n  lazy val propertyNames: Map[String, String] = Map(\n    \"backend.v1.url\" -> \"\",\n    \"backend.v1.appid\" -> \"\",\n    \"backend.v1.appkey\" -> \"\",\n    \"backend.v2.url\" -> \"\",\n    \"crashlytics.enabled\" -> \"false\",\n    \"crashlytics.apikey\" -> \"\",\n    \"crashlytics.apisecret\" -> \"\",\n    \"strictmode.enabled\" -> \"false\",\n    \"analytics.enabled\" -> \"false\",\n    \"analytics.trackid\" -> \"\",\n    \"firebase.enabled\" -> \"false\",\n    \"firebase.url\" -> \"\",\n    \"firebase.google.appid\" -> \"\",\n    \"firebase.google.apikey\" -> \"\",\n    \"firebase.gcm.senderid\" -> \"\",\n    \"firebase.clientid\" -> \"\",\n    \"flowup.enabled\" -> \"false\",\n    \"flowup.apikey\" -> \"\",\n    \"apptentive.enabled\" -> \"false\",\n    \"apptentive.apikey\" -> \"\")\n\n  lazy val propertiesFileName = sys.env.getOrElse(\"9CARDS_PROPERTIES\", \"ninecards.properties\")\n\n  lazy val propertiesMap = loadPropertiesMap\n\n  private[this] def loadPropertiesMap: Map[String, String] = {\n\n    def populateDefaultProperties(propertiesMap: Map[String, String]): Map[String, String] =\n      propertiesMap ++ (propertyNames filterKeys (key => !propertiesMap.contains(key)))\n\n    def loadPropertiesFile: Option[File] = {\n      val file = new File(propertiesFileName)\n      if (file.exists()) Some(file) else None\n    }\n\n    populateDefaultProperties {\n      (loadPropertiesFile map { file =>\n        val properties = new Properties()\n        properties.load(new FileInputStream(file))\n        properties.asScala.toMap\n      }) getOrElse Map.empty\n    }\n  }\n\n  def replaceValuesTask = Def.task[Seq[File]] {\n\n    val log = streams.value.log\n\n    log.debug(\"Replacing values\")\n    try {\n      val dir: (File, File) = (collectResources in Android).value\n      val valuesFile: File =  new File(dir._2, \"/values/values.xml\")\n      replaceContent(valuesFile, valuesFile)(log)\n      Seq(valuesFile)\n    } catch {\n      case e: Throwable =>\n        log.error(s\"An error occurred replacing values\")\n        throw e\n    }\n  }\n\n  def replaceContent(origin: File, target: File)(log: Logger) = {\n\n    def replaceLine(properties: Map[String, String], line: String) = {\n      @tailrec\n      def replace(properties: Map[String, String], line: String): String = {\n        properties.headOption match {\n          case Some(property) =>\n            val (key, value) = property\n            val name = s\"$${$key}\"\n            replace(properties.tail, if (line.contains(name)) line.replace(name, value) else line)\n          case None => line\n        }\n      }\n      replace(properties, line)\n    }\n\n    log.debug(s\"Loading properties file $propertiesFileName\")\n    val content = IO.readLines(origin) map (replaceLine(propertiesMap, _))\n    IO.write(target, content.mkString(\"\\n\"))\n  }\n\n}"
  },
  {
    "path": "project/S3.scala",
    "content": "import java.util.{Calendar, TimeZone, GregorianCalendar, Date}\n\nimport com.amazonaws.services.s3.model.GeneratePresignedUrlRequest\nimport com.amazonaws.{HttpMethod, Protocol, ClientConfiguration}\nimport com.amazonaws.auth.BasicAWSCredentials\nimport com.amazonaws.services.s3.AmazonS3Client\nimport sbt.Keys._\nimport sbt._\n\nimport scala.util.{Failure, Success, Try}\n\nobject S3 {\n\n  lazy val bucketName = sys.env.getOrElse(\"AWS_BUCKET\", \"\")\n\n  lazy val pullRequest = sys.env.getOrElse(\"GIT_PR\", \"false\")\n\n  lazy val hostName = s\"$bucketName.s3.amazonaws.com\"\n\n  lazy val apkName = pullRequest match {\n    case \"false\" => s\"nine-cards-v2-latest.apk\"\n    case number => s\"nine-cards-v2-$number.apk\"\n  }\n\n  def getExpirationDate: Date = {\n    val c = new GregorianCalendar(TimeZone.getTimeZone(\"UTC\"))\n    c.add(Calendar.DAY_OF_YEAR, 30)\n    c.getTime\n  }\n\n  val upload = TaskKey[Unit](\"s3-upload\", \"Uploads files to an S3 bucket.\")\n\n  val generateLink = TaskKey[Option[String]](\"s3-generateLink\",\"Uploads files to an S3 bucket.\")\n\n  lazy val credentials = new BasicAWSCredentials(\n    sys.env.getOrElse(\"AWS_ACCESS_KEY_ID\", \"\"),\n    sys.env.getOrElse(\"AWS_SECRET_KEY\", \"\"))\n\n  lazy val client = new AmazonS3Client(credentials, new ClientConfiguration().withProtocol(Protocol.HTTPS))\n\n  lazy val customS3Settings = Seq(\n    upload := {\n      val log = streams.value.log\n      log.info(\"Uploading APK\")\n      val file = target.value / \"android\" / \"output\" / \"nine-cards-v2-release.apk\"\n      Try(client.putObject(bucketName, apkName, file)) match {\n        case Success(r) => log.info(\"APK successfully uploaded\")\n        case Failure(e) =>\n          log.error(\"Error uploading APK\")\n          log.trace(e)\n      }\n    },\n    generateLink := {\n      val log = streams.value.log\n      val request = new GeneratePresignedUrlRequest(bucketName, apkName)\n      request.setMethod(HttpMethod.GET)\n      request.setExpiration(getExpirationDate)\n      val url = Try(client.generatePresignedUrl(request)) match {\n        case Success(result) =>\n          log.info(s\"URL successfully created: $result\")\n          Option(result.toString)\n        case Failure(e) =>\n          log.error(\"Error creating URL\")\n          log.trace(e)\n          None\n      }\n      url\n    }\n\n  )\n\n\n}"
  },
  {
    "path": "project/Settings.scala",
    "content": "import java.text.SimpleDateFormat\nimport java.util.Date\n\nimport Libraries.cats._\nimport Libraries.android._\nimport Libraries.graphics._\nimport Libraries.json._\nimport Libraries.macroid._\nimport Libraries.net._\nimport Libraries.google._\nimport Libraries.date._\nimport Libraries.test._\nimport Libraries.debug._\nimport Libraries.performance._\nimport android.Keys._\nimport android.PromptStorepassSigningConfig\nimport S3._\nimport Crashlytics._\nimport Libraries.monix._\nimport Proguard._\nimport sbt.Keys._\nimport sbt._\nimport microsites.MicrositeKeys._\nimport com.typesafe.sbt.site.SiteKeys\nimport microsites.MicrositesPlugin.autoImport.{micrositeExternalIncludesDirectory, micrositeExternalLayoutsDirectory}\nimport org.scalafmt.sbt.ScalaFmtPlugin.autoImport._\nimport com.typesafe.sbt.site.jekyll.JekyllPlugin.autoImport._\n\nobject Settings extends SiteKeys {\n\n  lazy val commit = sys.env.getOrElse(\"GIT_COMMIT\", \"unknown-commit\")\n\n  lazy val user = sys.env.getOrElse(\"USER\", \"unknown-user\")\n\n  val locationDebugKeystore = sys.props.get(\"debug\") match {\n    case Some(_) => \"ninecards.debug.keystore\"\n    case _ => Path.userHome.absolutePath + \"/.android/debug.keystore\"\n  }\n\n  println(s\"Using debug keystore: $locationDebugKeystore\")\n\n  def getDateFormatted = new SimpleDateFormat(\"yyyyMMdd\").format(new Date())\n\n  def versionNameSuffix = sys.env.get(\"GIT_PR\") match {\n    case Some(\"false\") => s\"-master-$getDateFormatted-$commit\"\n    case Some(prNumber) => s\"-PR$prNumber-$commit\"\n    case None => \"\"\n  }\n\n  lazy val androidVersionName = \"2.0.11-rc2\"\n  lazy val androidVersionCode = 68\n\n  // App Module\n  lazy val appSettings = basicSettings ++ multiDex ++ customS3Settings ++ crashlyticsSettings ++\n    Seq(\n      name := \"nine-cards-v2\",\n      versionName in Android := Some(s\"$androidVersionName$versionNameSuffix\"),\n      versionCode in Android := Some(androidVersionCode),\n      run <<= run in Android,\n      apkDebugSigningConfig in Android := PromptStorepassSigningConfig(\n        keystore = new File(locationDebugKeystore),\n        alias = \"androiddebugkey\"),\n      javacOptions in Compile ++= Seq(\"-target\", \"1.7\", \"-source\", \"1.7\"),\n      scalacOptions ++= Seq(\"-feature\", \"-deprecation\", \"-target:jvm-1.7\", \"-Yresolve-term-conflict:package\"),\n      transitiveAndroidLibs in Android := true,\n      libraryDependencies ++= appDependencies,\n      packagingOptions in Android := PackagingOptions(excludes = Seq(\n        \"META-INF/LICENSE\",\n        \"META-INF/LICENSE.txt\",\n        \"META-INF/NOTICE\",\n        \"META-INF/NOTICE.txt\",\n        \"scalac-plugin.xml\",\n        \"reference.conf\")),\n      dexMaxHeap in Android := \"2048m\",\n      proguardScala in Android := true,\n      useProguard in Android := true,\n      useProguardInDebug in Android := true,\n      proguardOptions in Android ++= proguardCommons,\n      proguardCache in Android := Seq.empty,\n      parallelExecution in Test := false)\n\n  // Api Module\n  lazy val apiSettings = basicSettings ++ librarySettings ++\n    Seq(libraryDependencies ++= apiDependencies)\n\n  // Repository Module\n  lazy val repositorySettings = basicSettings ++ librarySettings ++\n    Seq(libraryDependencies ++= repositoryDependencies)\n\n  // Services Module\n  lazy val servicesSettings = basicSettings ++ librarySettings ++\n    Seq(libraryDependencies ++= servicesDependencies)\n\n  // Process Module\n  lazy val processSettings = basicSettings ++ librarySettings ++\n    Seq(libraryDependencies ++= processDependencies)\n\n  // Commons Module\n  lazy val commonsSettings = basicSettings ++ librarySettings ++\n    Seq(libraryDependencies ++= commonsDependencies)\n\n  // Models Module\n  lazy val modelsSettings = basicSettings ++ librarySettings ++\n    Seq(libraryDependencies ++= modelsDependencies)\n\n  // Docs Module\n\n  lazy val micrositeSettings = Seq(\n    micrositeName := \"9Cards\",\n    micrositeDescription := \"A launcher crafted for and by Android Power Users\",\n    micrositeDocumentationUrl := \"/docs/\",\n    micrositeGithubOwner := \"47deg\",\n    micrositeGithubRepo := \"nine-cards-v2\",\n    micrositeExternalLayoutsDirectory := (resourceDirectory in Compile).value / \"microsite\" / \"layouts\",\n    micrositeExternalIncludesDirectory := (resourceDirectory in Compile).value / \"microsite\" / \"includes\",\n    includeFilter in Jekyll := (\"*.html\" | \"*.css\" | \"*.png\" | \"*.jpg\" | \"*.jpeg\" | \"*.gif\" | \"*.js\" | \"*.swf\" | \"*.md\" | \"*.webm\" | \"*.ico\" | \"*.mp4\" | \"CNAME\"),\n    micrositePalette := Map(\n      \"brand-primary\"     -> \"#E91E63\",\n      \"brand-secondary\"   -> \"#283593\",\n      \"brand-tertiary\"    -> \"#243087\",\n      \"gray-dark\"         -> \"#4A4A4A\",\n      \"gray\"              -> \"#797979\",\n      \"gray-light\"        -> \"#EAEAEA\",\n      \"gray-lighter\"      -> \"#F8F8F8\",\n      \"white-color\"       -> \"#FFFFFF\"))\n\n  // Commons Tests Module\n  lazy val commonsTestsSettings = basicSettings ++ librarySettings ++\n    Seq(libraryDependencies ++= commonsTestsDependencies)\n\n  // Android classes for Mock Module\n  lazy val mockAndroidSettings = basicSettings ++ librarySettings ++\n    Seq(libraryDependencies ++= mockAndroidDependencies)\n\n  // Basic Setting for all modules\n  lazy val basicSettings = Seq(\n    organization := \"cards.nine\",\n    organizationName := \"47deg\",\n    scalaVersion := Versions.scalaV,\n    resolvers ++= commonResolvers,\n    libraryDependencies ++= Seq(cats, monixTypes, monixEval, monixCats),\n    scalafmtConfig in ThisBuild := Some(file(\".scalafmt.conf\"))\n  ) ++ reformatOnCompileSettings\n\n  lazy val duplicatedFiles = Set(\n    \"AndroidManifest.xml\",\n    \"theme_dark.json\",\n    \"theme_light.json\")\n\n  // Settings associated to library modules\n  lazy val librarySettings = Seq(\n    mappings in(Compile, packageBin) ~= {\n      _.filter { tuple =>\n        !duplicatedFiles.contains(tuple._1.getName)\n      }\n    },\n    exportJars := true,\n    scalacOptions in Compile ++= Seq(\"-deprecation\", \"-Xexperimental\"),\n    javacOptions in Compile ++= Seq(\"-target\", \"1.7\", \"-source\", \"1.7\"),\n    javacOptions in Compile += \"-deprecation\",\n    proguardScala in Android := false)\n\n  lazy val appDependencies = Seq(\n    aar(androidAppCompat),\n    aar(macroidExtras),\n    aar(macroidRoot),\n    aar(androidSupportv4),\n    aar(androidRecyclerview),\n    aar(androidCardView),\n    aar(androidDesign),\n    aar(androidFlexbox),\n    aar(playServicesBase),\n    aar(playServicesAnalytics),\n    aar(playServicesDrive),\n    aar(playServicesAuth),\n    aar(playServicesPlus),\n    aar(playServicesAwareness),\n    aar(multiDexLib),\n    aar(crashlytics),\n    aar(firebaseCore),\n    aar(firebaseMessaging),\n    aar(flowUp),\n    prettyTime,\n    glide,\n    okHttp,\n    apptentive,\n    stetho,\n    stethoOkhttp,\n    stethoUrlconnection,\n    specs2,\n    mockito,\n    androidTest)\n\n  lazy val processDependencies = Seq(\n    androidProvidedLib,\n    specs2,\n    mockito)\n\n  lazy val servicesDependencies = Seq(\n    gfcTimeUUID,\n    androidProvidedLib,\n    specs2,\n    mockito)\n\n  lazy val apiDependencies = Seq(\n    androidProvidedLib,\n    playJson,\n    okHttp,\n    specs2,\n    mockito)\n\n  lazy val repositoryDependencies = Seq(\n    jodaTime,\n    androidProvidedLib,\n    specs2,\n    mockito)\n\n  lazy val commonsDependencies = Seq(\n    androidProvidedLib,\n    specs2,\n    mockito)\n\n  lazy val modelsDependencies = Seq(\n    androidProvidedLib,\n    playJson,\n    specs2,\n    mockito)\n\n  lazy val commonsTestsDependencies = Seq(\n    androidProvidedLib,\n    specs2Lib,\n    mockitoLib)\n\n  lazy val mockAndroidDependencies = Seq(\n    androidProvidedLib,\n    specs2Lib,\n    mockitoLib)\n\n  lazy val commonResolvers = Seq(\n    Resolver.mavenLocal,\n    DefaultMavenRepository,\n    Resolver.typesafeRepo(\"releases\"),\n    Resolver.typesafeRepo(\"snapshots\"),\n    Resolver.typesafeIvyRepo(\"snapshots\"),\n    Resolver.sonatypeRepo(\"releases\"),\n    Resolver.sonatypeRepo(\"snapshots\"),\n    Resolver.defaultLocal,\n    Resolver.jcenterRepo,\n    \"Scalaz Bintray Repo\" at \"http://dl.bintray.com/scalaz/releases\",\n    \"crashlytics\" at \"https://maven.fabric.io/public\"\n  )\n\n  lazy val multiDex = Seq(\n    dexMulti in Android := true,\n    dexMainClasses in Android := multiDexClasses\n  )\n\n  lazy val multiDexClasses = Seq(\n    \"cards/nine/app/NineCardsApplication.class\",\n    \"android/support/multidex/BuildConfig.class\",\n    \"android/support/multidex/MultiDex$V14.class\",\n    \"android/support/multidex/MultiDex$V19.class\",\n    \"android/support/multidex/MultiDex$V4.class\",\n    \"android/support/multidex/MultiDex.class\",\n    \"android/support/multidex/MultiDexApplication.class\",\n    \"android/support/multidex/MultiDexExtractor$1.class\",\n    \"android/support/multidex/MultiDexExtractor.class\",\n    \"android/support/multidex/ZipUtil$CentralDirectory.class\",\n    \"android/support/multidex/ZipUtil.class\")\n}\n"
  },
  {
    "path": "project/Versions.scala",
    "content": "object Versions {\n\n  val appV = \"1.0.0\"\n  val scalaV = \"2.11.7\"\n  val androidPlatformV = \"android-24\"\n  val androidV = \"25.0.1\"\n  val androidProvidedV = \"4.1.1.4\"\n  val flexboxV = \"0.2.3\"\n  val multiDexV = \"1.0.0\"\n  val macroidV = \"2.0\"\n  val playJsonV = \"2.3.7\"\n  val specs2V = \"2.4.15\"\n  val playServicesV = \"10.0.1\"\n  val mockitoV = \"3.0-M2\"\n  val glideV = \"3.7.0\"\n  val sprayClientV = \"1.3.1\"\n  val okHttpV = \"3.4.1\"\n  val prettyTimeV = \"3.2.7.Final\"\n  val mockServerV = \"3.9.2\"\n  val stethoV = \"1.3.1\"\n  val crashlyticsV = \"2.5.2\"\n  val apptentiveV = \"3.3.0\"\n  val jodaTimeV = \"2.9.2\"\n  val gfcTimeUUIDV = \"0.0.6\"\n  val monixV = \"2.1.2\"\n  val catsV = \"0.8.1\"\n  val flowUpV= \"0.3.0\"\n}\n"
  },
  {
    "path": "project/build.properties",
    "content": "sbt.version=0.13.13"
  },
  {
    "path": "project/plugins.sbt",
    "content": "addSbtPlugin(\"org.brianmckenna\" % \"sbt-wartremover\" % \"0.13\")\n\naddSbtPlugin(\"org.scala-android\" % \"sbt-android\" % \"1.7.4\")\n\naddSbtPlugin(\"com.geirsson\" % \"sbt-scalafmt\" % \"0.4.10\")\n\nresolvers += Resolver.url(\"scoverage-bintray\",\n                          url(\"https://dl.bintray.com/sksamuel/sbt-plugins/\"))(\n  Resolver.ivyStylePatterns)\n\naddSbtPlugin(\"org.scoverage\" % \"sbt-scoverage\" % \"1.3.0\")\n\nresolvers += \"Fabric public\" at \"https://maven.fabric.io/public\"\n\nlibraryDependencies ++= Seq(\n  \"com.amazonaws\" % \"aws-java-sdk-s3\" % \"1.10.44\",\n  \"org.apache.httpcomponents\" % \"httpclient\" % \"4.5.1\",\n  \"org.apache.httpcomponents\" % \"httpmime\" % \"4.5.1\",\n  \"io.fabric.tools\" % \"gradle\" % \"1.21.4\")\n\naddSbtPlugin(\"com.fortysevendeg\" % \"sbt-microsites\" % \"0.4.0\")\n\naddSbtPlugin(\"net.virtual-void\" % \"sbt-dependency-graph\" % \"0.8.2\")\n"
  },
  {
    "path": "project/proguard.sbt",
    "content": "libraryDependencies += \"net.sf.proguard\" % \"proguard-base\" % \"5.1\"\n"
  },
  {
    "path": "scripts/decrypt-keys.sh",
    "content": "#!/bin/sh\n\nopenssl aes-256-cbc -K $encrypted_0085ba44c983_key -iv $encrypted_0085ba44c983_iv -in travis-deploy-key.enc -out travis-deploy-key -d;\nchmod 600 travis-deploy-key;\ncp travis-deploy-key ~/.ssh/id_rsa;"
  },
  {
    "path": "scripts/publishMicrosite.sh",
    "content": "#!/bin/bash\nset -e\n\ngit config --global user.email \"developer@47deg.com\"\ngit config --global user.name \"47Deg (Travis CI)\"\ngit config --global push.default simple\n\nsbt docs/publishMicrosite"
  }
]