├── .github
└── workflows
│ ├── bintray-publish.yml
│ └── gradle-build.yml
├── .gitignore
├── App integration flow.PNG
├── LICENSE
├── README.md
├── android-webui-sdk.iml
├── build.gradle
├── demoapp
├── .gitignore
├── build.gradle
├── demoapp.iml
├── proguard-rules.pro
└── src
│ ├── library
│ ├── java
│ │ └── com
│ │ │ └── queue_it
│ │ │ └── shopdemo
│ │ │ ├── DeepActivity.java
│ │ │ ├── FirstFragment.java
│ │ │ ├── MainActivity.java
│ │ │ ├── ResultActivity.java
│ │ │ └── SecondFragment.java
│ └── res
│ │ └── layout
│ │ ├── activity_deep.xml
│ │ ├── activity_main.xml
│ │ ├── activity_result.xml
│ │ ├── content_deep.xml
│ │ ├── fragment_first.xml
│ │ └── fragment_second.xml
│ ├── library_androidx
│ ├── java
│ │ └── com
│ │ │ └── queue_it
│ │ │ └── shopdemo
│ │ │ ├── DeepActivity.java
│ │ │ ├── FirstFragment.java
│ │ │ ├── MainActivity.java
│ │ │ ├── ResultActivity.java
│ │ │ └── SecondFragment.java
│ └── res
│ │ └── layout
│ │ ├── activity_deep.xml
│ │ ├── activity_main.xml
│ │ ├── activity_result.xml
│ │ ├── content_deep.xml
│ │ ├── fragment_first.xml
│ │ └── fragment_second.xml
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── queue_it
│ │ └── shopdemo
│ │ └── TextValidator.java
│ └── res
│ ├── drawable
│ ├── ic_check_black_24dp.xml
│ ├── ic_error_black_24dp.xml
│ ├── ic_play_arrow_white_48px.xml
│ └── ic_replay_white_48px.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── navigation
│ └── nav_graph.xml
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── demowithprotectedapi
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── demowithprotectedapi
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── demowithprotectedapi
│ │ │ ├── FirstFragment.java
│ │ │ ├── MainActivity.java
│ │ │ ├── api
│ │ │ ├── Product.java
│ │ │ ├── ProductFilter.java
│ │ │ └── ProductsService.java
│ │ │ ├── exceptions
│ │ │ └── MustBeQueued.java
│ │ │ ├── http
│ │ │ ├── AddCookiesInterceptor.java
│ │ │ ├── CookieStorage.java
│ │ │ ├── QueueITInterceptor.java
│ │ │ ├── ReceivedCookiesInterceptor.java
│ │ │ └── UserAgentInterceptor.java
│ │ │ └── repos
│ │ │ ├── IProductRepository.java
│ │ │ ├── ProductRepository.java
│ │ │ └── RetrofitProductRepository.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── content_main.xml
│ │ └── fragment_first.xml
│ │ ├── menu
│ │ └── menu_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── navigation
│ │ └── nav_graph.xml
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── demowithprotectedapi
│ └── ExampleUnitTest.java
├── documentation
└── protected_apis.md
├── gradle.properties
├── gradle
└── wrapper
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── .gitignore
├── build.gradle
├── library.iml
├── proguard-rules.pro
└── src
│ ├── library
│ └── java
│ │ └── com
│ │ └── queue_it
│ │ └── androidsdk
│ │ ├── QueueActivity.java
│ │ └── WaitingRoomStateBroadcaster.java
│ ├── library_androidx
│ └── java
│ │ └── com
│ │ └── queue_it
│ │ └── androidsdk
│ │ ├── QueueActivity.java
│ │ └── WaitingRoomStateBroadcaster.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── queue_it
│ │ │ └── androidsdk
│ │ │ ├── Error.java
│ │ │ ├── IUriOverrider.java
│ │ │ ├── IWaitingRoomStateBroadcaster.java
│ │ │ ├── QueueActivityBase.java
│ │ │ ├── QueueCache.java
│ │ │ ├── QueueDisabledInfo.java
│ │ │ ├── QueueITApiClient.java
│ │ │ ├── QueueITApiClientListener.java
│ │ │ ├── QueueITEngine.java
│ │ │ ├── QueueITException.java
│ │ │ ├── QueueITWaitingRoomProvider.java
│ │ │ ├── QueueITWaitingRoomProviderListener.java
│ │ │ ├── QueueITWaitingRoomView.java
│ │ │ ├── QueueItEngineOptions.java
│ │ │ ├── QueueListener.java
│ │ │ ├── QueuePassedInfo.java
│ │ │ ├── QueueService.java
│ │ │ ├── QueueServiceListener.java
│ │ │ ├── QueueTryPassResult.java
│ │ │ ├── QueueUrlHelper.java
│ │ │ ├── RedirectType.java
│ │ │ ├── UriOverrideWrapper.java
│ │ │ ├── UriOverrider.java
│ │ │ └── UserAgentManager.java
│ └── res
│ │ ├── layout
│ │ └── activity_queue.xml
│ │ └── values
│ │ └── strings.xml
│ └── test
│ ├── java
│ └── com
│ │ └── queue_it
│ │ └── androidsdk
│ │ └── UriOverriderTest.java
│ └── resources
│ └── robolectric.properties
├── publish.gradle
└── settings.gradle
/.github/workflows/bintray-publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to bintray on closed PR
2 | on:
3 | release:
4 | types: [released]
5 |
6 | jobs:
7 | gradle:
8 | strategy:
9 | matrix:
10 | os: [ubuntu-latest]
11 | runs-on: ${{ matrix.os }}
12 | steps:
13 | - uses: actions/checkout@v2
14 | # Export properties
15 | - name: Setup bintray credentials
16 | env:
17 | BINTRAY_APIKEY: ${{ secrets.BINTRAY_APIKEY }}
18 | run: |
19 | echo "bintray.user=queueitdevs" > ./local.properties
20 | echo "bintray.apiKey=${BINTRAY_APIKEY}" >> ./local.properties
21 | - uses: actions/setup-java@v1
22 | with:
23 | java-version: 11
24 | - uses: eskatos/gradle-command-action@v1
25 | with:
26 | arguments: assembleLibrary_androidx :library:assembleLibrary bintrayUpload
--------------------------------------------------------------------------------
/.github/workflows/gradle-build.yml:
--------------------------------------------------------------------------------
1 | name: Run Gradle Build on Push
2 | on: push
3 | jobs:
4 | gradle:
5 | strategy:
6 | matrix:
7 | os: [ubuntu-latest]
8 | runs-on: ${{ matrix.os }}
9 | steps:
10 | - uses: actions/checkout@v2
11 | - name: Create local.properties
12 | run: echo "" >> ./local.properties
13 | - uses: actions/setup-java@v1
14 | with:
15 | java-version: 11
16 | - uses: eskatos/gradle-command-action@v1
17 | with:
18 | arguments: build
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | /.idea/libraries
5 | .DS_Store
6 | /build
7 | /captures
8 | /.idea/caches/build_file_checksums.ser
9 | /.idea/caches/gradle_models.ser
10 | /.idea/codeStyles/Project.xml
11 | /.vs/android-webui-sdk/v16/.suo
12 | /.vs/slnx.sqlite
13 | /.vs/VSWorkspaceState.json
14 | /private_key_sender.asc
15 | /public_key_sender.asc
16 | .idea
17 | *.gpg
18 | **/build
19 | node_modules
20 | .classpath
21 | .project
22 | *.prefs
23 | dist
--------------------------------------------------------------------------------
/App integration flow.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/App integration flow.PNG
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Queue-it
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [  ](https://repo1.maven.org/maven2/com/queue-it/androidsdk/)
2 |
3 | # Queue-it Android WebUI SDK
4 |
5 | Library for integrating Queue-it's virtual waiting room into an Android app written in java.
6 |
7 | ## Sample app
8 |
9 | A sample app to try out functionality in the library can be found on the [Releases](https://github.com/queueit/android-sdk/releases) page.
10 | This sample app uses the first approach of integration calling QueueITEngine run method.
11 |
12 | ## Installation
13 |
14 | Before starting please download the whitepaper "Mobile App Integration" from GO Queue-it Platform.
15 | This whitepaper contains the needed information to perform a successful integration.
16 |
17 | Using Gradle:
18 |
19 | ```gradle
20 | implementation 'com.queue-it.androidsdk:library:2.1.4'
21 | //For AndroidX
22 | //implementation 'com.queue-it.androidsdk:library-androidx:2.1.4'
23 | ```
24 |
25 | ## How to use the library (Mobile SDK integration only, no API protection)
26 |
27 | As the App developer, you must manage the state (whether the user was previously queued up or not) inside the app's storage.
28 |
29 | After you have received the **onQueuePassed** callback, the app must remember to keep the state, possibly with a date/time expiration.
30 | When the user wants to navigate to specific screens on the app which needs Queue-it protection, your code check this state/variable, and only call SDK methods / **QueueITEngine.run** in the case where the user did not previously queue up.
31 |
32 | Please note that when the user clicks back to navigate back to a protected screen, the same check needs to be done.
33 |
34 | ### Simple SDK integration using run method.
35 | The simplest mobile SDK integration requires you to call one method, run() before showing the protected screen and potentially calling server API which needs peak traffic protection.
36 | Invoke QueueITEngine as per example below. Parameters `layoutName`, `language` and `options` are optional.
37 |
38 | ```java
39 | QueueITEngine engine = new QueueITEngine(YourActivity.this, customerId, eventIdOrAlias, layoutName, language,
40 | new QueueListener() {
41 |
42 | // This callback will be called when the user has been through the queue.
43 | // Here you should store session information, so user will only be sent to queue again if the session has timed out.
44 | @Override
45 | public void onQueuePassed(QueuePassedInfo queuePassedInfo) {
46 | }
47 |
48 | // This callback will be called just before the webview (hosting the queue page) will be shown.
49 | // Here you can change some relevant UI elements.
50 | @Override
51 | public void onQueueViewWillOpen() {
52 | }
53 |
54 | // This callback will be called when the queue used (event alias ID) is in the 'disabled' state.
55 | // Most likely the application should still function, but the queue's 'disabled' state can be changed at any time,
56 | // so session handling is important.
57 | @Override
58 | public void onQueueDisabled(QueueDisabledInfo queueDisabledInfo) {
59 | }
60 |
61 | // This callback will be called when the mobile application can't reach Queue-it's servers.
62 | // Most likely because the mobile device has no internet connection.
63 | // Here you decide if the application should function or not now that is has no queue-it protection.
64 | @Override
65 | public void onQueueItUnavailable() {
66 | }
67 |
68 | // This callback will be called when the mobile application can't reach Queue-it's servers.
69 | // It can be any one of these scenarios:
70 | // 1) Queue-it's servers can't be reached (connectivity issue).
71 | // 2) SSL connection error if custom queue domain is used having an invalid certificate.
72 | // 3) Client receives HTTP 4xx response.
73 | // In all these cases is most likely a misconfiguration of the queue settings:
74 | // Invalid customer ID, event alias ID or cname setting on queue (GO Queue-it portal -> event settings).
75 | @Override
76 | public void onError(Error error, String errorMessage) {
77 | } // Called on connectivity problems
78 |
79 | // This callback will be called after a user clicks a close link in the layout and the WebView closes.
80 | // The close link is "queueit://close". Whenever the user navigates to this link, the SDK intercepts the navigation
81 | // and closes the WebView.
82 | @Override
83 | public void onWebViewClosed(){
84 | }
85 |
86 | // This callback will be called when the user clicks on a link to restart the session.
87 | // The link is 'queueit://restartSession'. Whenever the user navigates to this link, the SDK intercepts the navigation,
88 | // closes the WebView, clears the URL cache and calls this callback.
89 | // In this callback you would normally call run/runWithToken/runWithKey in order to restart the queueing.
90 | @Override
91 | public void onSessionRestart(QueueITEngine queueITEngine) {
92 | }
93 | });
94 |
95 | try {
96 | engine.run(YourActivity.this);
97 | }
98 | catch (QueueITException e) { } // Gets thrown when a request is already in progress. In general you can ignore this.
99 | ```
100 |
101 |
102 |
103 | 
104 |
105 |
106 | ### QueueITEngine options
107 |
108 | The QueueITEngine can be configured if you use the `options` argument in it's constructor. Here's an example.
109 |
110 | ```java
111 | QueueItEngineOptions options = new QueueItEngineOptions();
112 | // Use this if you want to disable the back button when the waiting room is shown
113 | options.setBackButtonDisabledFromWR(true);
114 | ```
115 |
116 | ## Mobile SDK integration with tryPass and showQueue methods:
117 |
118 | If you need finner granularity control over the mobile integration, you can use tryPass and showQue instead of just using run method which will open a webview to the Queue when needed.
119 |
120 | This provides you more control of the logic before potentially opening the webview and showing the Queue page, as well as more control over the webview showing the queue page.
121 |
122 | ### Checking status of Waiting room
123 |
124 | It is possible to get the status of a waiting room to make sure it is ready to be visited. To do this, one of the below methods from **QueueITWaitingRoomProvider** class could be used.
125 |
126 | - tryPass
127 | - tryPassWithEnqueueToken
128 | - tryPassWithEnqueueKey
129 |
130 | Calling any of these methods will result in executing **onSuccess** or **onFailure** callbacks. These two callbacks must be provided by implementing **QueueITWaitingRoomProviderListener** interface and passed to the constructor of **QueueITWaitingRoomProvider** class, and will lead to below:
131 |
132 | - If **isPassedThrough()** retuns true, queueittoken and more information will be available as an argument to **OnSuccess** function with type of **QueueTryPassResult**.
133 | - If **isPassedThrough()** returns false, it means that the waiting room is active. The waitingroom page should be shown to the visitor by calling **showQueue** method of the **QueueITWaitingRoomView**, then the visitor will wait for its turn. The **showQueue** method needs **QueryTryPassResult** object from **OnSuccess** function.
134 |
135 | ## Showing the queue page to visitor
136 |
137 | When waiting room is queueing the visitors, each visitor has to visit the waiting room page once. The queue page could be shown to visitors when it is necessary using **showQueue** method of **QueueITWaitingRoomView** class.
138 | Before calling **showQueue**, the status of the waiting room should be already retrieved as described in [Get waiting room status](#Get-waiting-room-status) to make sure that the waiting room is ready.
139 |
140 | sample code for showing the queue page:
141 |
142 | ```java
143 | QueueITWaitingRoomView queueITWaitingRoomView = new QueueITWaitingRoomView(MainActivity.this, queueListener, queueItEngineOptions);
144 | queueITWaitingRoomView.showQueue(_queuePassedInfo.getQueueUrl(), _queuePassedInfo.getTargetUrl());
145 | ```
146 | ## Mobile SDK Integration with proteced API (Queue-it connector on server side):
147 | If your application is using an API that's protected by a Queue-it connector (KnownUser) you can check out [this documentation](https://github.com/queueit/android-webui-sdk/blob/master/documentation/protected_apis.md).
148 |
149 | ## Required permissions
150 |
151 | ```xml
152 |
153 |
154 | ```
155 |
156 | ## Activities to include in your manifest
157 |
158 | ```xml
159 |
160 | ```
--------------------------------------------------------------------------------
/android-webui-sdk.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | mavenCentral()
7 | maven {
8 | url "https://plugins.gradle.org/m2/"
9 | }
10 | }
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:7.1.1'
13 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
14 | classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.22.0"
15 | classpath "de.marcphilipp.gradle:nexus-publish-plugin:0.4.0"
16 | }
17 | }
18 | apply plugin: 'io.codearte.nexus-staging'
19 |
20 | Properties localProps = new Properties()
21 | if (rootProject.file("local.properties").exists()) {
22 | localProps.load(rootProject.file("local.properties").newDataInputStream())
23 | }
24 |
25 | subprojects {
26 | tasks.withType(Javadoc).all { enabled = false }
27 | }
28 |
29 | allprojects {
30 | repositories {
31 | google()
32 | mavenCentral()
33 | }
34 |
35 | ext {
36 | groupId = 'com.queue-it.androidsdk'
37 | libraryName = 'com.queue_it.androidsdk'
38 | libraryDescription = 'Android SDK to integrate with Queue-it'
39 | libraryVersion = "2.1.7"
40 | organization = "Queue-it"
41 | organizationUrl = "https://queue-it.com"
42 |
43 | artifact = 'library'
44 | siteUrl = "https://github.com/queueit/android-webui-sdk"
45 | gitUrl = "https://github.com/queueit/android-webui-sdk.git"
46 |
47 | licenseName = 'MIT'
48 | licenseUrl = 'MIT'
49 | allLicenses = ["MIT"]
50 | var = '7.0.0-alpha05'
51 | extraProperties = localProps
52 |
53 | OSSRH_USERNAME = localProps.getProperty("OSSRH_USERNAME") ?: System.getenv("OSSRH_USERNAME")
54 | OSSRH_PASSWORD = localProps.getProperty("OSSRH_PASSWORD") ?: System.getenv("OSSRH_PASSWORD")
55 | PGP_KEY = localProps.getProperty("PGP_KEY") ?: System.getenv("PGP_KEY")
56 | PGP_PASSWORD = localProps.getProperty("PGP_PASSWORD") ?: System.getenv("PGP_PASSWORD")
57 | }
58 | }
59 |
60 | task clean(type: Delete) {
61 | delete rootProject.buildDir
62 | }
--------------------------------------------------------------------------------
/demoapp/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/demoapp/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 30
5 | buildToolsVersion "30.0.3"
6 |
7 | defaultConfig {
8 | applicationId "com.queue_it.shopdemo"
9 | minSdkVersion 15
10 | targetSdkVersion 30
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 |
21 |
22 | flavorDimensions "androidx"
23 | productFlavors{
24 | library_androidx
25 | //Note that this requires useAndroidx=false
26 | library
27 | }
28 | lint {
29 | abortOnError false
30 | checkReleaseBuilds false
31 | }
32 | }
33 |
34 | dependencies {
35 | implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
36 | //implementation project(path: ':library')
37 | //library_androidxImplementation 'com.queue-it.androidsdk:library-androidx:2.0.34'
38 | library_androidxImplementation project(path: ':library')
39 | //libraryImplementation 'com.queue-it.androidsdk:library:2.0.34'
40 | libraryImplementation project(path: ':library')
41 |
42 | testImplementation 'junit:junit:4.13'
43 |
44 | library_androidxImplementation 'androidx.appcompat:appcompat:1.2.0'
45 | library_androidxImplementation 'androidx.constraintlayout:constraintlayout:2.0.4'
46 | library_androidxImplementation 'androidx.navigation:navigation-fragment:2.3.3'
47 | library_androidxImplementation 'androidx.navigation:navigation-ui:2.3.3'
48 |
49 | libraryImplementation 'com.google.android.material:material:1.3.0'
50 | //noinspection GradleCompatible
51 | libraryImplementation 'com.android.support:appcompat-v7:28.0.0'
52 | }
53 |
--------------------------------------------------------------------------------
/demoapp/demoapp.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | generateDebugSources
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/demoapp/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Users\MortenBrixPedersen\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/demoapp/src/library/java/com/queue_it/shopdemo/DeepActivity.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.shopdemo;
2 |
3 | import android.os.Bundle;
4 | import com.google.android.material.floatingactionbutton.FloatingActionButton;
5 | import com.google.android.material.snackbar.Snackbar;
6 |
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.support.v7.widget.Toolbar;
9 | import android.view.View;
10 |
11 | public class DeepActivity extends AppCompatActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_deep);
17 | Toolbar toolbar = findViewById(R.id.toolbar);
18 | setSupportActionBar(toolbar);
19 |
20 | FloatingActionButton fab = findViewById(R.id.fab);
21 | fab.setOnClickListener(new View.OnClickListener() {
22 | @Override
23 | public void onClick(View view) {
24 | Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
25 | .setAction("Action", null).show();
26 | }
27 | });
28 | }
29 | }
--------------------------------------------------------------------------------
/demoapp/src/library/java/com/queue_it/shopdemo/FirstFragment.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.shopdemo;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.NonNull;
5 | import android.support.v4.app.Fragment;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 |
11 | public class FirstFragment extends Fragment {
12 |
13 | @Override
14 | public View onCreateView(
15 | LayoutInflater inflater, ViewGroup container,
16 | Bundle savedInstanceState
17 | ) {
18 | // Inflate the layout for this fragment
19 | return inflater.inflate(R.layout.fragment_first, container, false);
20 | }
21 |
22 | public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
23 | super.onViewCreated(view, savedInstanceState);
24 |
25 | view.findViewById(R.id.button_first).setOnClickListener(new View.OnClickListener() {
26 | @Override
27 | public void onClick(View view) {
28 | // NavHostFragment.findNavController(FirstFragment.this)
29 | // .navigate(R.id.action_FirstFragment_to_SecondFragment);
30 | }
31 | });
32 | }
33 | }
--------------------------------------------------------------------------------
/demoapp/src/library/java/com/queue_it/shopdemo/ResultActivity.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.shopdemo;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import com.google.android.material.floatingactionbutton.FloatingActionButton;
6 |
7 | import android.support.v4.content.res.ResourcesCompat;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.view.View;
10 | import android.widget.ImageView;
11 | import android.widget.TextView;
12 |
13 |
14 | public class ResultActivity extends AppCompatActivity {
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | setContentView(R.layout.activity_result);
19 |
20 | String result;
21 | boolean success;
22 | if (savedInstanceState == null) {
23 | Bundle extras = getIntent().getExtras();
24 | if (extras == null) {
25 | result = null;
26 | success = false;
27 | } else {
28 | result = extras.getString("result");
29 | success = extras.getBoolean("success");
30 | }
31 | } else {
32 | result = (String) savedInstanceState.getSerializable("result");
33 | success = (Boolean) savedInstanceState.getSerializable("success");
34 | }
35 |
36 | final TextView resultText = (TextView)findViewById(R.id.result_text);
37 | final ImageView checkedImageView = (ImageView)findViewById(R.id.image_view_checked);
38 | final FloatingActionButton retry_button = (FloatingActionButton)findViewById(R.id.retry_button);
39 |
40 | resultText.setText(result);
41 | if (success){
42 | checkedImageView.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_check_black_24dp, null));
43 | } else {
44 | checkedImageView.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_error_black_24dp, null));
45 | }
46 |
47 | retry_button.setOnClickListener(new View.OnClickListener() {
48 | @Override
49 | public void onClick(View v) {
50 | Intent intent = new Intent(ResultActivity.this, MainActivity.class);
51 | startActivity(intent);
52 | finish();
53 | }
54 | });
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/demoapp/src/library/java/com/queue_it/shopdemo/SecondFragment.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.shopdemo;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.NonNull;
5 | import android.support.v4.app.Fragment;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | public class SecondFragment extends Fragment {
11 |
12 | @Override
13 | public View onCreateView(
14 | LayoutInflater inflater, ViewGroup container,
15 | Bundle savedInstanceState
16 | ) {
17 | // Inflate the layout for this fragment
18 | return inflater.inflate(R.layout.fragment_second, container, false);
19 | }
20 |
21 | public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
22 | super.onViewCreated(view, savedInstanceState);
23 |
24 | view.findViewById(R.id.button_second).setOnClickListener(new View.OnClickListener() {
25 | @Override
26 | public void onClick(View view) {
27 | // NavHostFragment.findNavController(SecondFragment.this)
28 | // .navigate(R.id.action_SecondFragment_to_FirstFragment);
29 | }
30 | });
31 | }
32 | }
--------------------------------------------------------------------------------
/demoapp/src/library/res/layout/activity_deep.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
32 |
33 |
--------------------------------------------------------------------------------
/demoapp/src/library/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
18 |
19 |
25 |
26 |
30 |
31 |
35 |
36 |
42 |
48 |
49 |
50 |
56 |
62 |
63 |
64 |
70 |
76 |
77 |
78 |
84 |
90 |
91 |
92 |
98 |
104 |
105 |
106 |
112 |
118 |
119 |
120 |
126 |
127 |
131 |
137 |
138 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
157 |
158 |
159 |
160 |
161 |
--------------------------------------------------------------------------------
/demoapp/src/library/res/layout/activity_result.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
18 |
19 |
24 |
25 |
29 |
30 |
35 |
36 |
43 |
44 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/demoapp/src/library/res/layout/content_deep.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demoapp/src/library/res/layout/fragment_first.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
28 |
--------------------------------------------------------------------------------
/demoapp/src/library/res/layout/fragment_second.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
27 |
--------------------------------------------------------------------------------
/demoapp/src/library_androidx/java/com/queue_it/shopdemo/DeepActivity.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.shopdemo;
2 |
3 | import android.os.Bundle;
4 | import com.google.android.material.floatingactionbutton.FloatingActionButton;
5 | import com.google.android.material.snackbar.Snackbar;
6 | import androidx.appcompat.app.AppCompatActivity;
7 | import androidx.appcompat.widget.Toolbar;
8 | import android.view.View;
9 |
10 | public class DeepActivity extends AppCompatActivity {
11 |
12 | @Override
13 | protected void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_deep);
16 | Toolbar toolbar = findViewById(R.id.toolbar);
17 | setSupportActionBar(toolbar);
18 |
19 | FloatingActionButton fab = findViewById(R.id.fab);
20 | fab.setOnClickListener(new View.OnClickListener() {
21 | @Override
22 | public void onClick(View view) {
23 | Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
24 | .setAction("Action", null).show();
25 | }
26 | });
27 | }
28 | }
--------------------------------------------------------------------------------
/demoapp/src/library_androidx/java/com/queue_it/shopdemo/FirstFragment.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.shopdemo;
2 |
3 | import android.os.Bundle;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.fragment.app.Fragment;
10 |
11 | import androidx.navigation.fragment.NavHostFragment;
12 |
13 | public class FirstFragment extends Fragment {
14 |
15 | @Override
16 | public View onCreateView(
17 | LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState
19 | ) {
20 | // Inflate the layout for this fragment
21 | return inflater.inflate(R.layout.fragment_first, container, false);
22 | }
23 |
24 | public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
25 | super.onViewCreated(view, savedInstanceState);
26 |
27 | view.findViewById(R.id.button_first).setOnClickListener(new View.OnClickListener() {
28 | @Override
29 | public void onClick(View view) {
30 | NavHostFragment.findNavController(FirstFragment.this)
31 | .navigate(R.id.action_FirstFragment_to_SecondFragment);
32 | }
33 | });
34 | }
35 | }
--------------------------------------------------------------------------------
/demoapp/src/library_androidx/java/com/queue_it/shopdemo/ResultActivity.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.shopdemo;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import com.google.android.material.floatingactionbutton.FloatingActionButton;
6 | import androidx.core.content.res.ResourcesCompat;
7 | import androidx.appcompat.app.AppCompatActivity;
8 | import android.view.View;
9 | import android.widget.ImageView;
10 | import android.widget.TextView;
11 |
12 |
13 | public class ResultActivity extends AppCompatActivity {
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.activity_result);
18 |
19 | String result;
20 | boolean success;
21 | if (savedInstanceState == null) {
22 | Bundle extras = getIntent().getExtras();
23 | if (extras == null) {
24 | result = null;
25 | success = false;
26 | } else {
27 | result = extras.getString("result");
28 | success = extras.getBoolean("success");
29 | }
30 | } else {
31 | result = (String) savedInstanceState.getSerializable("result");
32 | success = (Boolean) savedInstanceState.getSerializable("success");
33 | }
34 |
35 | final TextView resultText = (TextView)findViewById(R.id.result_text);
36 | final ImageView checkedImageView = (ImageView)findViewById(R.id.image_view_checked);
37 | final FloatingActionButton retry_button = (FloatingActionButton)findViewById(R.id.retry_button);
38 |
39 | resultText.setText(result);
40 | if (success){
41 | checkedImageView.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_check_black_24dp, null));
42 | } else {
43 | checkedImageView.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_error_black_24dp, null));
44 | }
45 |
46 | retry_button.setOnClickListener(new View.OnClickListener() {
47 | @Override
48 | public void onClick(View v) {
49 | Intent intent = new Intent(ResultActivity.this, MainActivity.class);
50 | startActivity(intent);
51 | finish();
52 | }
53 | });
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/demoapp/src/library_androidx/java/com/queue_it/shopdemo/SecondFragment.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.shopdemo;
2 |
3 | import android.os.Bundle;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.fragment.app.Fragment;
10 |
11 | import androidx.navigation.fragment.NavHostFragment;
12 |
13 | public class SecondFragment extends Fragment {
14 |
15 | @Override
16 | public View onCreateView(
17 | LayoutInflater inflater, ViewGroup container,
18 | Bundle savedInstanceState
19 | ) {
20 | // Inflate the layout for this fragment
21 | return inflater.inflate(R.layout.fragment_second, container, false);
22 | }
23 |
24 | public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
25 | super.onViewCreated(view, savedInstanceState);
26 |
27 | view.findViewById(R.id.button_second).setOnClickListener(new View.OnClickListener() {
28 | @Override
29 | public void onClick(View view) {
30 | NavHostFragment.findNavController(SecondFragment.this)
31 | .navigate(R.id.action_SecondFragment_to_FirstFragment);
32 | }
33 | });
34 | }
35 | }
--------------------------------------------------------------------------------
/demoapp/src/library_androidx/res/layout/activity_deep.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
32 |
33 |
--------------------------------------------------------------------------------
/demoapp/src/library_androidx/res/layout/activity_result.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
18 |
19 |
24 |
25 |
29 |
30 |
35 |
36 |
43 |
44 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/demoapp/src/library_androidx/res/layout/content_deep.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demoapp/src/library_androidx/res/layout/fragment_first.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
28 |
--------------------------------------------------------------------------------
/demoapp/src/library_androidx/res/layout/fragment_second.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
27 |
--------------------------------------------------------------------------------
/demoapp/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/demoapp/src/main/java/com/queue_it/shopdemo/TextValidator.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.shopdemo;
2 |
3 | import android.text.Editable;
4 | import android.text.TextWatcher;
5 | import android.widget.TextView;
6 |
7 | public abstract class TextValidator implements TextWatcher {
8 | private final TextView textView;
9 |
10 | public TextValidator(TextView textView) {
11 | this.textView = textView;
12 | }
13 |
14 | public abstract void validate(TextView textView, String text);
15 |
16 | @Override
17 | final public void afterTextChanged(Editable s) {
18 | String text = textView.getText().toString();
19 | validate(textView, text);
20 | }
21 |
22 | @Override
23 | final public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
24 |
25 | @Override
26 | final public void onTextChanged(CharSequence s, int start, int before, int count) { }
27 | }
--------------------------------------------------------------------------------
/demoapp/src/main/res/drawable/ic_check_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/demoapp/src/main/res/drawable/ic_error_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/demoapp/src/main/res/drawable/ic_play_arrow_white_48px.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demoapp/src/main/res/drawable/ic_replay_white_48px.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demoapp/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demoapp/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demoapp/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demoapp/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demoapp/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demoapp/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demoapp/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demoapp/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demoapp/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demoapp/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demoapp/src/main/res/navigation/nav_graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
17 |
18 |
23 |
24 |
27 |
28 |
--------------------------------------------------------------------------------
/demoapp/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/demoapp/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3daf2c
4 | #1c9800
5 | #3F51B5
6 |
7 |
--------------------------------------------------------------------------------
/demoapp/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 |
7 |
--------------------------------------------------------------------------------
/demoapp/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Queue-it Shop Demo
3 | DeepActivity
4 |
5 | First Fragment
6 | Second Fragment
7 | Next
8 | Previous
9 |
10 | Hello first fragment
11 | Hello second fragment. Arg: %1$s
12 |
13 |
--------------------------------------------------------------------------------
/demoapp/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/demowithprotectedapi/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/demowithprotectedapi/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | android {
6 | compileSdkVersion 30
7 | buildToolsVersion "30.0.3"
8 |
9 | defaultConfig {
10 | applicationId "com.example.demowithprotectedapi"
11 | minSdkVersion 21
12 | targetSdkVersion 30
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | compileOptions {
26 | sourceCompatibility JavaVersion.VERSION_1_8
27 | targetCompatibility JavaVersion.VERSION_1_8
28 | }
29 | buildFeatures {
30 | viewBinding true
31 | }
32 |
33 | flavorDimensions "androidx"
34 | productFlavors{
35 | library_androidx
36 | //Note that this requires useAndroidx=false
37 | library
38 | }
39 | }
40 |
41 | dependencies {
42 |
43 | implementation 'androidx.appcompat:appcompat:1.2.0'
44 | implementation 'com.google.android.material:material:1.3.0'
45 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
46 | implementation 'androidx.navigation:navigation-fragment:2.3.5'
47 | implementation 'androidx.navigation:navigation-ui:2.3.5'
48 | // retrofit
49 | implementation 'com.squareup.retrofit2:retrofit:2.1.0'
50 | implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
51 | //Queue-it
52 | //implementation project(path: ':library')
53 | library_androidxImplementation project(path: ':library')
54 |
55 | testImplementation 'junit:junit:4.+'
56 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
57 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
58 | }
--------------------------------------------------------------------------------
/demowithprotectedapi/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/demowithprotectedapi/src/androidTest/java/com/example/demowithprotectedapi/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 | assertEquals("com.example.demowithprotectedapi", appContext.getPackageName());
25 | }
26 | }
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/FirstFragment.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi;
2 |
3 | import android.net.Uri;
4 | import android.os.AsyncTask;
5 | import android.os.Bundle;
6 | import android.os.Handler;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.Toast;
11 |
12 | import androidx.annotation.NonNull;
13 | import androidx.fragment.app.Fragment;
14 |
15 | import com.example.demowithprotectedapi.api.Product;
16 | import com.example.demowithprotectedapi.databinding.FragmentFirstBinding;
17 | import com.example.demowithprotectedapi.exceptions.MustBeQueued;
18 | import com.example.demowithprotectedapi.repos.IProductRepository;
19 | import com.example.demowithprotectedapi.repos.RetrofitProductRepository;
20 | import com.queue_it.androidsdk.Error;
21 | import com.queue_it.androidsdk.QueueDisabledInfo;
22 | import com.queue_it.androidsdk.QueueITApiClient;
23 | import com.queue_it.androidsdk.QueueITEngine;
24 | import com.queue_it.androidsdk.QueueITException;
25 | import com.queue_it.androidsdk.QueueListener;
26 | import com.queue_it.androidsdk.QueuePassedInfo;
27 |
28 | import java.io.IOException;
29 | import java.io.UnsupportedEncodingException;
30 | import java.net.URLDecoder;
31 | import java.nio.charset.StandardCharsets;
32 | import java.util.concurrent.atomic.AtomicBoolean;
33 |
34 | public class FirstFragment extends Fragment {
35 |
36 | private FragmentFirstBinding binding;
37 | private IProductRepository _productRepo;
38 | private final Object queuedLock = new Object();
39 | private final AtomicBoolean _queuePassed = new AtomicBoolean(false);
40 |
41 | @Override
42 | public View onCreateView(
43 | @NonNull LayoutInflater inflater, ViewGroup container,
44 | Bundle savedInstanceState
45 | ) {
46 | _productRepo = new RetrofitProductRepository("https://fastly.v3.ticketania.com");
47 | binding = FragmentFirstBinding.inflate(inflater, container, false);
48 | return binding.getRoot();
49 | }
50 |
51 | public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
52 | super.onViewCreated(view, savedInstanceState);
53 |
54 | binding.buttonFirst.setOnClickListener(view1 -> {
55 | ProductHandler productHandler = new ProductHandler();
56 | productHandler.execute();
57 | });
58 | }
59 |
60 | @Override
61 | public void onDestroyView() {
62 | super.onDestroyView();
63 | binding = null;
64 | }
65 |
66 | private void queueUser(String value) {
67 | try {
68 | Uri valueUri = Uri.parse(URLDecoder.decode(value, StandardCharsets.UTF_8.name()));
69 | String customerId = valueUri.getQueryParameter("c");
70 | String wrId = valueUri.getQueryParameter("e");
71 | QueueITApiClient.IsTest = true;
72 | final QueueITEngine q = new QueueITEngine(MainActivity.getInstance(), customerId, wrId, "", "", new QueueListener() {
73 | @Override
74 | protected void onSessionRestart(QueueITEngine queueITEngine) {
75 | try {
76 | queueITEngine.run(MainActivity.getInstance());
77 | } catch (QueueITException e) {
78 | e.printStackTrace();
79 | }
80 |
81 | }
82 |
83 | @Override
84 | protected void onQueuePassed(QueuePassedInfo queuePassedInfo) {
85 | _productRepo.addQueueToken(queuePassedInfo.getQueueItToken());
86 | _queuePassed.set(true);
87 | Toast.makeText(MainActivity.getInstance(), "You passed the queue! You can try again.", Toast.LENGTH_SHORT).show();
88 | synchronized (queuedLock) {
89 | queuedLock.notify();
90 | }
91 | }
92 |
93 | @Override
94 | public void onQueueViewWillOpen() {
95 | Toast.makeText(MainActivity.getInstance(), "onQueueViewWillOpen", Toast.LENGTH_SHORT).show();
96 | }
97 |
98 | @Override
99 | public void onUserExited() {
100 | Toast.makeText(MainActivity.getInstance(), "onUserExited", Toast.LENGTH_SHORT).show();
101 | }
102 |
103 | @Override
104 | public void onQueueDisabled(QueueDisabledInfo queueDisabledInfo) {
105 | Toast.makeText(MainActivity.getInstance(), "The queue is disabled. Your token: " + queueDisabledInfo.getQueueItToken()
106 | , Toast.LENGTH_SHORT).show();
107 | _productRepo.addQueueToken(queueDisabledInfo.getQueueItToken());
108 | _queuePassed.set(true);
109 | synchronized (queuedLock) {
110 | queuedLock.notify();
111 | }
112 | }
113 |
114 | @Override
115 | public void onQueueItUnavailable() {
116 | Toast.makeText(MainActivity.getInstance(), "Queue-it is unavailable", Toast.LENGTH_SHORT).show();
117 | }
118 |
119 | @Override
120 | public void onError(Error error, String errorMessage) {
121 | Toast.makeText(MainActivity.getInstance(), "Critical error: " + errorMessage, Toast.LENGTH_SHORT).show();
122 | }
123 |
124 | @Override
125 | public void onWebViewClosed() {
126 | Toast.makeText(MainActivity.getInstance(), "WebView closed", Toast.LENGTH_SHORT).show();
127 | }
128 | });
129 | q.run(MainActivity.getInstance());
130 | } catch (QueueITException | UnsupportedEncodingException e) {
131 | e.printStackTrace();
132 | }
133 | }
134 |
135 | public class ProductHandler extends AsyncTask {
136 |
137 |
138 | @Override
139 | protected Product doInBackground(Void[] objects) {
140 | synchronized (queuedLock) {
141 | try {
142 | return _productRepo.getProduct();
143 | } catch (IOException e) {
144 | if (!(e instanceof MustBeQueued)) {
145 | e.printStackTrace();
146 | }
147 | assert e instanceof MustBeQueued;
148 | Handler handler = new Handler(MainActivity.getInstance().getMainLooper());
149 | handler.post(() -> queueUser(((MustBeQueued) e).getValue()));
150 |
151 | //Maybe wait for completion and repeat the call? This is optional.
152 | try {
153 | while (!_queuePassed.get()) {
154 | queuedLock.wait();
155 | }
156 | Thread.sleep(1000);
157 | return _productRepo.getProduct();
158 | } catch (InterruptedException | IOException ex) {
159 | ex.printStackTrace();
160 | }
161 | }
162 | }
163 |
164 | return null;
165 | }
166 |
167 | @Override
168 | protected void onPostExecute(Product p) {
169 | if (p == null) {
170 | Toast.makeText(MainActivity.getInstance(), "Couldn't fetch product", Toast.LENGTH_SHORT).show();
171 | } else {
172 | Toast.makeText(MainActivity.getInstance(), "Fetched product: " + p.getName(), Toast.LENGTH_SHORT).show();
173 | }
174 | }
175 | }
176 | }
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi;
2 |
3 | import android.os.Bundle;
4 |
5 | import com.google.android.material.snackbar.Snackbar;
6 |
7 | import androidx.appcompat.app.AppCompatActivity;
8 |
9 | import android.view.View;
10 |
11 | import androidx.navigation.NavController;
12 | import androidx.navigation.Navigation;
13 | import androidx.navigation.ui.AppBarConfiguration;
14 | import androidx.navigation.ui.NavigationUI;
15 |
16 | import com.example.demowithprotectedapi.databinding.ActivityMainBinding;
17 |
18 | import android.view.Menu;
19 | import android.view.MenuItem;
20 |
21 | public class MainActivity extends AppCompatActivity {
22 |
23 | private AppBarConfiguration appBarConfiguration;
24 | private ActivityMainBinding binding;
25 | private static MainActivity _instance;
26 |
27 | public static MainActivity getInstance(){
28 | return _instance;
29 | }
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | _instance = this;
34 | super.onCreate(savedInstanceState);
35 |
36 | binding = ActivityMainBinding.inflate(getLayoutInflater());
37 | setContentView(binding.getRoot());
38 |
39 | setSupportActionBar(binding.toolbar);
40 |
41 | NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
42 | appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
43 | NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
44 |
45 | binding.fab.setOnClickListener(new View.OnClickListener() {
46 | @Override
47 | public void onClick(View view) {
48 | Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
49 | .setAction("Action", null).show();
50 | }
51 | });
52 | }
53 |
54 | @Override
55 | public boolean onCreateOptionsMenu(Menu menu) {
56 | // Inflate the menu; this adds items to the action bar if it is present.
57 | getMenuInflater().inflate(R.menu.menu_main, menu);
58 | return true;
59 | }
60 |
61 | @Override
62 | public boolean onOptionsItemSelected(MenuItem item) {
63 | // Handle action bar item clicks here. The action bar will
64 | // automatically handle clicks on the Home/Up button, so long
65 | // as you specify a parent activity in AndroidManifest.xml.
66 | int id = item.getItemId();
67 |
68 | //noinspection SimplifiableIfStatement
69 | if (id == R.id.action_settings) {
70 | return true;
71 | }
72 |
73 | return super.onOptionsItemSelected(item);
74 | }
75 |
76 | @Override
77 | public boolean onSupportNavigateUp() {
78 | NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
79 | return NavigationUI.navigateUp(navController, appBarConfiguration)
80 | || super.onSupportNavigateUp();
81 | }
82 | }
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/api/Product.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi.api;
2 |
3 | public class Product {
4 | String name;
5 |
6 | public String getName() {
7 | return name;
8 | }
9 |
10 | public void setName(String name) {
11 | this.name = name;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/api/ProductFilter.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi.api;
2 |
3 | public class ProductFilter {
4 | public String id;
5 | }
6 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/api/ProductsService.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi.api;
2 |
3 | import java.util.List;
4 |
5 | import retrofit2.Call;
6 | import retrofit2.http.Body;
7 | import retrofit2.http.GET;
8 | import retrofit2.http.POST;
9 |
10 | public interface ProductsService {
11 | @GET("safeaction?queue-event1-nodomain=t")
12 | Call getProduct();
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/exceptions/MustBeQueued.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi.exceptions;
2 |
3 | import java.io.IOException;
4 |
5 | public class MustBeQueued extends IOException {
6 | private final String value;
7 |
8 | public MustBeQueued(String s) {
9 | super("Must be queued in: " + s);
10 | this.value = s;
11 | }
12 |
13 | public String getValue() {
14 | return value;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/http/AddCookiesInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi.http;
2 |
3 | import android.util.Log;
4 |
5 | import java.io.IOException;
6 | import java.util.HashSet;
7 |
8 | import okhttp3.Interceptor;
9 | import okhttp3.Request;
10 | import okhttp3.Response;
11 |
12 | public class AddCookiesInterceptor implements Interceptor {
13 |
14 | private final CookieStorage _storage;
15 |
16 | public AddCookiesInterceptor(CookieStorage storage){
17 | _storage = storage;
18 | }
19 |
20 | @Override
21 | public Response intercept(Chain chain) throws IOException {
22 | Request.Builder builder = chain.request().newBuilder();
23 | HashSet preferences = _storage.getCookies();
24 | for (String cookie : preferences) {
25 | builder.addHeader("Cookie", cookie);
26 | Log.v("OkHttp", "Adding Header: " + cookie); // This is done so I know which headers are being added; this interceptor is used after the normal logging of OkHttp
27 | }
28 |
29 | return chain.proceed(builder.build());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/http/CookieStorage.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi.http;
2 |
3 | import java.util.HashSet;
4 |
5 | public class CookieStorage {
6 | private HashSet _cookies;
7 |
8 | public CookieStorage() {
9 | _cookies = new HashSet<>();
10 | }
11 |
12 | public void store(HashSet cookies) {
13 | _cookies = cookies;
14 | }
15 |
16 | public HashSet getCookies() {
17 | return _cookies;
18 | }
19 |
20 | public void clear() {
21 | _cookies.clear();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/http/QueueITInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi.http;
2 |
3 | import com.example.demowithprotectedapi.exceptions.MustBeQueued;
4 |
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | import java.io.IOException;
8 |
9 | import okhttp3.Headers;
10 | import okhttp3.HttpUrl;
11 | import okhttp3.Interceptor;
12 | import okhttp3.Request;
13 | import okhttp3.Response;
14 |
15 | public class QueueITInterceptor implements Interceptor {
16 |
17 | private final CookieStorage _cookies;
18 | private String _queueItToken;
19 |
20 | public QueueITInterceptor(CookieStorage cookies) {
21 | _cookies = cookies;
22 | }
23 |
24 | public void setToken(String token){
25 | _queueItToken = token;
26 | }
27 |
28 | @NotNull
29 | @Override
30 | public Response intercept(@NotNull Chain chain) throws IOException {
31 | HttpUrl.Builder urlBuilder = chain.request().url().newBuilder();
32 | if (_queueItToken!=null && _queueItToken.length() > 0) {
33 | urlBuilder.addQueryParameter("queueittoken", _queueItToken);
34 | }
35 |
36 | Request chainReq = chain.request();
37 | Request req = chainReq.newBuilder()
38 | .addHeader("x-queueit-ajaxpageurl", chainReq.url().toString())
39 | .url(urlBuilder.build())
40 | .build();
41 |
42 | Response res = chain.proceed(req);
43 | if (mustQueue(res)) {
44 | resetToken();
45 | _cookies.clear();
46 | Headers resHeaders = res.headers();
47 | throw new MustBeQueued(resHeaders.get("x-queueit-redirect"));
48 | }
49 |
50 | return res;
51 | }
52 |
53 | public void resetToken() {
54 | _queueItToken = null;
55 | }
56 |
57 | public boolean mustQueue(Response response) {
58 | Headers responseHeaders = response.headers();
59 | return responseHeaders.names().contains("x-queueit-redirect");
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/http/ReceivedCookiesInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi.http;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.io.IOException;
6 | import java.util.HashSet;
7 |
8 | import okhttp3.HttpUrl;
9 | import okhttp3.Interceptor;
10 | import okhttp3.Request;
11 | import okhttp3.Response;
12 |
13 | public class ReceivedCookiesInterceptor implements Interceptor {
14 |
15 | private final CookieStorage _storage;
16 |
17 | public ReceivedCookiesInterceptor(CookieStorage storage) {
18 | _storage = storage;
19 | }
20 |
21 | @Override
22 | public Response intercept(Chain chain) throws IOException {
23 | Response originalResponse = chain.proceed(chain.request());
24 |
25 | if (!originalResponse.headers("Set-Cookie").isEmpty()) {
26 | HashSet cookies = new HashSet<>();
27 |
28 | for (String header : originalResponse.headers("Set-Cookie")) {
29 | cookies.add(header);
30 | }
31 |
32 | _storage.store(cookies);
33 | }
34 | if (originalResponse.priorResponse() != null && !originalResponse.priorResponse().headers("Set-Cookie").isEmpty()) {
35 | HashSet cookies = new HashSet<>();
36 |
37 | for (String header : originalResponse.priorResponse().headers("Set-Cookie")) {
38 | cookies.add(header);
39 | }
40 |
41 | _storage.store(cookies);
42 | }
43 |
44 | return originalResponse;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/http/UserAgentInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi.http;
2 |
3 | import android.os.Build;
4 |
5 | import java.io.IOException;
6 | import java.util.Locale;
7 |
8 | import okhttp3.Interceptor;
9 | import okhttp3.Request;
10 | import okhttp3.Response;
11 |
12 | public class UserAgentInterceptor implements Interceptor {
13 |
14 | public final String userAgent;
15 |
16 | public UserAgentInterceptor(String userAgent) {
17 | this.userAgent = userAgent;
18 | }
19 |
20 | public UserAgentInterceptor(String appName, String appVersion) {
21 | this(String.format(Locale.US,
22 | "%s/%s (Android %s; %s; %s %s; %s)",
23 | appName,
24 | appVersion,
25 | Build.VERSION.RELEASE,
26 | Build.MODEL,
27 | Build.BRAND,
28 | Build.DEVICE,
29 | Locale.getDefault().getLanguage()));
30 | }
31 |
32 | @Override
33 | public Response intercept(Interceptor.Chain chain) throws IOException {
34 | Request userAgentRequest = chain.request()
35 | .newBuilder()
36 | .header("User-Agent", userAgent)
37 | .build();
38 | return chain.proceed(userAgentRequest);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/repos/IProductRepository.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi.repos;
2 |
3 | import com.example.demowithprotectedapi.exceptions.MustBeQueued;
4 | import com.example.demowithprotectedapi.api.Product;
5 |
6 | import java.io.IOException;
7 |
8 | public interface IProductRepository {
9 | Product getProduct() throws IOException, MustBeQueued;
10 |
11 | void addQueueToken(String queueItToken);
12 | }
13 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/repos/ProductRepository.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi.repos;
2 |
3 | import com.example.demowithprotectedapi.exceptions.MustBeQueued;
4 | import com.example.demowithprotectedapi.api.Product;
5 | import com.example.demowithprotectedapi.http.AddCookiesInterceptor;
6 | import com.example.demowithprotectedapi.http.CookieStorage;
7 | import com.example.demowithprotectedapi.http.QueueITInterceptor;
8 | import com.example.demowithprotectedapi.http.ReceivedCookiesInterceptor;
9 | import com.example.demowithprotectedapi.http.UserAgentInterceptor;
10 | import com.google.gson.Gson;
11 |
12 | import java.io.IOException;
13 |
14 | import okhttp3.Headers;
15 | import okhttp3.OkHttpClient;
16 | import okhttp3.Request;
17 | import okhttp3.Response;
18 |
19 | public class ProductRepository implements IProductRepository {
20 | private final OkHttpClient _client;
21 | private final String _baseUrl;
22 | private final QueueITInterceptor _queueItInterceptor;
23 |
24 | public ProductRepository() {
25 | _baseUrl = "https://fastly.v3.ticketania.com";
26 | CookieStorage cookies = new CookieStorage();
27 | _queueItInterceptor = new QueueITInterceptor(cookies);
28 | _client = new OkHttpClient().newBuilder()
29 | .addInterceptor(_queueItInterceptor)
30 | .addInterceptor(new AddCookiesInterceptor(cookies))
31 | .addInterceptor(new ReceivedCookiesInterceptor(cookies))
32 | .hostnameVerifier((hostname, session) -> true)
33 | .build();
34 | }
35 |
36 | private Request.Builder getRequestBuilder() {
37 | return new Request.Builder()
38 | .header("x-queueit-ajaxpageurl", "someValue");
39 | }
40 |
41 | public Product getProduct() throws IOException, MustBeQueued {
42 | String fullURL = _baseUrl + "/safeaction?queue-event1-nodomain=t&";
43 | Request request = getRequestBuilder()
44 | .url(fullURL)
45 | .build();
46 | Response response = _client.newCall(request).execute();
47 | if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
48 | return new Gson().fromJson(response.body().string(), Product.class);
49 | }
50 |
51 | public void addQueueToken(String queueItToken) {
52 | _queueItInterceptor.setToken(queueItToken);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/java/com/example/demowithprotectedapi/repos/RetrofitProductRepository.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi.repos;
2 |
3 | import com.example.demowithprotectedapi.api.Product;
4 | import com.example.demowithprotectedapi.api.ProductFilter;
5 | import com.example.demowithprotectedapi.api.ProductsService;
6 | import com.example.demowithprotectedapi.http.AddCookiesInterceptor;
7 | import com.example.demowithprotectedapi.http.CookieStorage;
8 | import com.example.demowithprotectedapi.http.QueueITInterceptor;
9 | import com.example.demowithprotectedapi.http.ReceivedCookiesInterceptor;
10 | import com.example.demowithprotectedapi.http.UserAgentInterceptor;
11 |
12 | import java.io.IOException;
13 |
14 | import okhttp3.OkHttpClient;
15 | import retrofit2.Call;
16 | import retrofit2.Retrofit;
17 | import retrofit2.converter.gson.GsonConverterFactory;
18 |
19 | public class RetrofitProductRepository implements IProductRepository {
20 | private final ProductsService _service;
21 | private final String _baseUrl;
22 | private final QueueITInterceptor _queueItInterceptor;
23 |
24 | public RetrofitProductRepository(String baseUrl) {
25 | _baseUrl = baseUrl;
26 | CookieStorage cookies = new CookieStorage();
27 | _queueItInterceptor = new QueueITInterceptor(cookies);
28 | OkHttpClient client = new OkHttpClient.Builder()
29 | .addInterceptor(_queueItInterceptor)
30 | .addInterceptor(new AddCookiesInterceptor(cookies))
31 | .addInterceptor(new ReceivedCookiesInterceptor(cookies))
32 | .addInterceptor(new UserAgentInterceptor("demoapp", "1.0.0"))
33 | .hostnameVerifier((hostname, session) -> true)
34 | .build();
35 |
36 |
37 | Retrofit retrofit = new Retrofit.Builder()
38 | .client(client)
39 | .baseUrl(_baseUrl)
40 | .addConverterFactory(GsonConverterFactory.create())
41 | .build();
42 | _service = retrofit.create(ProductsService.class);
43 | }
44 |
45 | public Product getProduct() throws IOException {
46 | Call pCall = _service.getProduct();
47 | retrofit2.Response response = pCall.execute();
48 | return response.body();
49 | }
50 |
51 | public void addQueueToken(String queueItToken) {
52 | _queueItInterceptor.setToken(queueItToken);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
32 |
33 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/layout/fragment_first.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
28 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demowithprotectedapi/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demowithprotectedapi/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demowithprotectedapi/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demowithprotectedapi/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demowithprotectedapi/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demowithprotectedapi/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demowithprotectedapi/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demowithprotectedapi/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demowithprotectedapi/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/demowithprotectedapi/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/navigation/nav_graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | demoWithProtectedApi
3 | Settings
4 |
5 | First Fragment
6 | Second Fragment
7 | Next
8 | Previous
9 |
10 | Hello first fragment
11 | Hello second fragment. Arg: %1$s
12 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/demowithprotectedapi/src/test/java/com/example/demowithprotectedapi/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.example.demowithprotectedapi;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/documentation/protected_apis.md:
--------------------------------------------------------------------------------
1 | # Using connector protected APIs with the SDK
2 |
3 | ## Sample app
4 |
5 | A sample app that shows an example of this integration can be found in the [demowithprotectedapi](https://github.com/queueit/android-webui-sdk/tree/master/demowithprotectedapi) directory.
6 | There are a few OkHTTP interceptors in the `http` package that can be easily integrated.
7 |
8 | ## Implementation
9 |
10 | To integrate with a protected API we need to handle the validation responses that we may get in case the user should be queued.
11 | All calls to protected APIs need to include the `x-queueit-ajaxpageurl` header with a non-empty value and a Queue-it accepted cookie (if present).
12 | The integration can be described in the following steps:
13 |
14 | 1. API Request with is made
15 | 2. We get a response which may be the API response or a notice that the user should be queued
16 | 3. Scenario 1, user should be queued
17 | 3.1. If the user should be queued we'll get a 302 response with a `x-queueit-redirect` header. We need to extract the `c`(Customer ID) and `e`(Waiting Room ID) query string parameters from the header and call `QueueITEngine.run` with them, just as you would normally do with the SDK.
18 | 3.2. We wait for the `onQueuePassed` callback and we store the QueueITToken.
19 | 3.3. We can repeat the API request appending the `queueittoken={QueueITToken}` query string parameter.
20 | 3.4. We store the Queue-it cookies from the response, so they can be set in other API calls.
21 |
22 | 4. Scenario 2, user should not be queued
23 | 4.1. We store the Queue-it cookies from the response
24 |
25 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 | #android.enableJetifier=true
20 | android.useAndroidX=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/queueit/android-webui-sdk/574a4e879eca8a715f9b7dcb2734e38a0ebfc466/gradlew
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 30
5 | buildToolsVersion "30.0.3"
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 30
10 |
11 | buildConfigField 'String', 'VERSION_NAME', "\"$libraryVersion\""
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 |
20 | testOptions {
21 | unitTests {
22 | includeAndroidResources = true
23 | returnDefaultValues = true
24 | }
25 | }
26 |
27 |
28 | flavorDimensions "androidx"
29 | productFlavors{
30 | library_androidx
31 | library
32 | }
33 | lint {
34 | abortOnError false
35 | }
36 | }
37 |
38 | dependencies {
39 | api fileTree(dir: 'libs', include: ['*.jar'])
40 |
41 | testImplementation 'junit:junit:4.13'
42 | testImplementation 'org.mockito:mockito-core:2.28.2'
43 | testImplementation 'pl.pragmatists:JUnitParams:1.0.5'
44 | testImplementation 'org.robolectric:robolectric:4.4'
45 |
46 | implementation 'com.squareup.okhttp3:okhttp:4.9.2'
47 | library_androidxApi 'androidx.appcompat:appcompat:1.2.0'
48 | library_androidxApi 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
49 |
50 | //noinspection GradleCompatible
51 | libraryImplementation 'com.android.support:appcompat-v7:28.0.0'
52 | }
53 |
54 | apply plugin: 'maven-publish'
55 |
56 | group = groupId
57 | version = libraryVersion
58 |
59 | task sourcesJar(type: Jar) {
60 | classifier = 'sources'
61 | from android.sourceSets.main.java.srcDirs
62 | }
63 |
64 | task javadoc(type: Javadoc) {
65 | source = android.sourceSets.main.java.srcDirs
66 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
67 | }
68 |
69 | task javadocJar(type: Jar, dependsOn: javadoc) {
70 | classifier = 'javadoc'
71 | from javadoc.destinationDir
72 | }
73 |
74 | artifacts {
75 | archives javadocJar
76 | archives sourcesJar
77 | }
78 |
79 | nexusStaging {
80 | stagingProfileId = "219f8fe296e7e"
81 | packageGroup = groupId
82 | username = OSSRH_USERNAME
83 | password = OSSRH_PASSWORD
84 | }
85 |
86 | apply from: '../publish.gradle' // Makes this library publishable
--------------------------------------------------------------------------------
/library/library.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | generateDebugSources
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Users\MortenBrixPedersen\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/library/src/library/java/com/queue_it/androidsdk/QueueActivity.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 |
6 | public class QueueActivity extends Activity {
7 |
8 | private QueueActivityBase base = new QueueActivityBase(this);
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | base.initialize(savedInstanceState);
14 | }
15 |
16 | @Override
17 | public void onBackPressed() {
18 | if (base.getOptions().isBackButtonDisabledFromWR()) {
19 | return;
20 | }
21 | super.onBackPressed();
22 | }
23 |
24 | @Override
25 | protected void onSaveInstanceState(Bundle outState) {
26 | super.onSaveInstanceState(outState);
27 | base.saveInstanceState(outState);
28 | }
29 |
30 | @Override
31 | protected void onDestroy() {
32 | base.destroy();
33 | super.onDestroy();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/library/src/library/java/com/queue_it/androidsdk/WaitingRoomStateBroadcaster.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 | import android.support.v4.content.LocalBroadcastManager;
8 |
9 | public class WaitingRoomStateBroadcaster implements IWaitingRoomStateBroadcaster {
10 |
11 | private final Context _context;
12 |
13 | public WaitingRoomStateBroadcaster(Context context) {
14 | _context = context;
15 | }
16 |
17 | public void registerReceivers(BroadcastReceiver onPassed,
18 | BroadcastReceiver onUrlChanged,
19 | BroadcastReceiver onActivityClosed,
20 | BroadcastReceiver onUserExited,
21 | BroadcastReceiver onError,
22 | BroadcastReceiver onWebViewClosed,
23 | BroadcastReceiver onSessionRestartReceiver) {
24 | LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(_context);
25 |
26 | localBroadcastManager.registerReceiver(onPassed, new IntentFilter("on-queue-passed"));
27 | localBroadcastManager.registerReceiver(onUrlChanged, new IntentFilter("on-changed-queue-url"));
28 | localBroadcastManager.registerReceiver(onActivityClosed, new IntentFilter("queue-activity-closed"));
29 | localBroadcastManager.registerReceiver(onUserExited, new IntentFilter("queue-user-exited"));
30 | localBroadcastManager.registerReceiver(onError, new IntentFilter("on-queue-error"));
31 | localBroadcastManager.registerReceiver(onWebViewClosed, new IntentFilter("on-webview-close"));
32 | localBroadcastManager.registerReceiver(onSessionRestartReceiver, new IntentFilter("on-session-restart"));
33 | }
34 |
35 | public void unregisterReceivers(BroadcastReceiver onPassed,
36 | BroadcastReceiver onUrlChanged,
37 | BroadcastReceiver onActivityClosed,
38 | BroadcastReceiver onUserExited,
39 | BroadcastReceiver onError,
40 | BroadcastReceiver onWebViewClosed,
41 | BroadcastReceiver onSessionRestartReceiver) {
42 | LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(_context);
43 |
44 | localBroadcastManager.unregisterReceiver(onPassed);
45 | localBroadcastManager.unregisterReceiver(onUrlChanged);
46 | localBroadcastManager.unregisterReceiver(onActivityClosed);
47 | localBroadcastManager.unregisterReceiver(onUserExited);
48 | localBroadcastManager.unregisterReceiver(onError);
49 | localBroadcastManager.unregisterReceiver(onWebViewClosed);
50 | localBroadcastManager.unregisterReceiver(onSessionRestartReceiver);
51 | }
52 |
53 | @Override
54 | public void broadcastChangedQueueUrl(String urlString) {
55 | Intent intentChangedQueueUrl = new Intent("on-changed-queue-url");
56 | intentChangedQueueUrl.putExtra("url", urlString);
57 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intentChangedQueueUrl);
58 | }
59 |
60 | @Override
61 | public void broadcastQueuePassed(String queueItToken) {
62 | Intent intent = new Intent("on-queue-passed");
63 | intent.putExtra("queue-it-token", queueItToken);
64 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
65 | }
66 |
67 | @Override
68 | public void broadcastQueueActivityClosed() {
69 | Intent intent = new Intent("queue-activity-closed");
70 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
71 | }
72 |
73 | @Override
74 | public void broadcastUserExited() {
75 | Intent intent = new Intent("queue-user-exited");
76 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
77 | }
78 |
79 | @Override
80 | public void broadcastQueueError(String errorMessage) {
81 | Intent intent = new Intent("on-queue-error");
82 | intent.putExtra("error-message", errorMessage);
83 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
84 | }
85 |
86 | @Override
87 | public void broadcastWebViewClosed() {
88 | Intent intent = new Intent("on-webview-close");
89 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
90 | }
91 |
92 | @Override
93 | public void broadcastOnSessionRestart() {
94 | Intent intent = new Intent("on-session-restart");
95 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/library/src/library_androidx/java/com/queue_it/androidsdk/QueueActivity.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 |
6 | public class QueueActivity extends Activity {
7 | private QueueActivityBase base = new QueueActivityBase(this);
8 |
9 | @Override
10 | protected void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 | base.initialize(savedInstanceState);
13 | }
14 |
15 | @Override
16 | public void onBackPressed() {
17 | if (base.getOptions().isBackButtonDisabledFromWR()) {
18 | return;
19 | }
20 | super.onBackPressed();
21 | }
22 |
23 | @Override
24 | protected void onSaveInstanceState(Bundle outState) {
25 | super.onSaveInstanceState(outState);
26 | base.saveInstanceState(outState);
27 | }
28 |
29 | @Override
30 | protected void onDestroy() {
31 | base.destroy();
32 | super.onDestroy();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/library/src/library_androidx/java/com/queue_it/androidsdk/WaitingRoomStateBroadcaster.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 |
8 | import androidx.localbroadcastmanager.content.LocalBroadcastManager;
9 |
10 | public class WaitingRoomStateBroadcaster implements IWaitingRoomStateBroadcaster {
11 |
12 | private final Context _context;
13 |
14 | public WaitingRoomStateBroadcaster(Context context) {
15 | _context = context;
16 | }
17 |
18 | public void registerReceivers(BroadcastReceiver onPassed,
19 | BroadcastReceiver onUrlChanged,
20 | BroadcastReceiver onActivityClosed,
21 | BroadcastReceiver onUserExited,
22 | BroadcastReceiver onError,
23 | BroadcastReceiver onWebViewClosed,
24 | BroadcastReceiver onSessionRestartReceiver) {
25 | LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(_context);
26 |
27 | localBroadcastManager.registerReceiver(onPassed, new IntentFilter("on-queue-passed"));
28 | localBroadcastManager.registerReceiver(onUrlChanged, new IntentFilter("on-changed-queue-url"));
29 | localBroadcastManager.registerReceiver(onActivityClosed, new IntentFilter("queue-activity-closed"));
30 | localBroadcastManager.registerReceiver(onUserExited, new IntentFilter("queue-user-exited"));
31 | localBroadcastManager.registerReceiver(onError, new IntentFilter("on-queue-error"));
32 | localBroadcastManager.registerReceiver(onWebViewClosed, new IntentFilter("on-webview-close"));
33 | localBroadcastManager.registerReceiver(onSessionRestartReceiver, new IntentFilter("on-session-restart"));
34 | }
35 |
36 | public void unregisterReceivers(BroadcastReceiver onPassed,
37 | BroadcastReceiver onUrlChanged,
38 | BroadcastReceiver onActivityClosed,
39 | BroadcastReceiver onUserExited,
40 | BroadcastReceiver onError,
41 | BroadcastReceiver onWebViewClosed,
42 | BroadcastReceiver onSessionRestartReceiver) {
43 | LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(_context);
44 |
45 | localBroadcastManager.unregisterReceiver(onPassed);
46 | localBroadcastManager.unregisterReceiver(onUrlChanged);
47 | localBroadcastManager.unregisterReceiver(onActivityClosed);
48 | localBroadcastManager.unregisterReceiver(onUserExited);
49 | localBroadcastManager.unregisterReceiver(onError);
50 | localBroadcastManager.unregisterReceiver(onWebViewClosed);
51 | localBroadcastManager.unregisterReceiver(onSessionRestartReceiver);
52 | }
53 |
54 | @Override
55 | public void broadcastChangedQueueUrl(String urlString) {
56 | Intent intentChangedQueueUrl = new Intent("on-changed-queue-url");
57 | intentChangedQueueUrl.putExtra("url", urlString);
58 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intentChangedQueueUrl);
59 | }
60 |
61 | @Override
62 | public void broadcastQueuePassed(String queueItToken) {
63 | Intent intent = new Intent("on-queue-passed");
64 | intent.putExtra("queue-it-token", queueItToken);
65 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
66 | }
67 |
68 | @Override
69 | public void broadcastQueueActivityClosed() {
70 | Intent intent = new Intent("queue-activity-closed");
71 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
72 | }
73 |
74 | @Override
75 | public void broadcastUserExited() {
76 | Intent intent = new Intent("queue-user-exited");
77 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
78 | }
79 |
80 | @Override
81 | public void broadcastQueueError(String errorMessage) {
82 | Intent intent = new Intent("on-queue-error");
83 | intent.putExtra("error-message", errorMessage);
84 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
85 | }
86 |
87 | @Override
88 | public void broadcastWebViewClosed() {
89 | Intent intent = new Intent("on-webview-close");
90 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
91 | }
92 |
93 | @Override
94 | public void broadcastOnSessionRestart() {
95 | Intent intent = new Intent("on-session-restart");
96 | LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/Error.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | public enum Error
4 | {
5 | NO_CONNECTION,
6 | INVALID_RESPONSE,
7 | SSL_ERROR,
8 | Queueit_NotAvailable
9 | }
10 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/IUriOverrider.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.net.Uri;
4 | import android.webkit.WebView;
5 |
6 | public interface IUriOverrider {
7 | Uri getQueue();
8 |
9 | void setQueue(Uri queue);
10 |
11 | Uri getTarget();
12 |
13 | void setTarget(Uri target);
14 |
15 | String getUserId();
16 |
17 | void setUserId(String userId);
18 |
19 | boolean handleNavigationRequest(String uriString, WebView webview, UriOverrideWrapper uriOverride);
20 | }
21 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/IWaitingRoomStateBroadcaster.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.content.BroadcastReceiver;
4 |
5 | public interface IWaitingRoomStateBroadcaster {
6 | void broadcastChangedQueueUrl(String urlString);
7 |
8 | void broadcastQueuePassed(String queueItToken);
9 |
10 | void broadcastQueueActivityClosed();
11 |
12 | void broadcastUserExited();
13 |
14 | void broadcastQueueError(String errorMessage);
15 |
16 | void broadcastWebViewClosed();
17 |
18 | void broadcastOnSessionRestart();
19 |
20 | void registerReceivers(BroadcastReceiver queuePassedBroadcastReceiver,
21 | BroadcastReceiver queueUrlChangedBroadcastReceiver,
22 | BroadcastReceiver queueActivityClosedBroadcastReceiver,
23 | BroadcastReceiver queueUserExitedBroadcastReceiver,
24 | BroadcastReceiver queueErrorBroadcastReceiver,
25 | BroadcastReceiver webViewClosedBroadcastReceiver,
26 | BroadcastReceiver onSessionRestartReceiver);
27 |
28 | void unregisterReceivers(BroadcastReceiver queuePassedBroadcastReceiver,
29 | BroadcastReceiver queueUrlChangedBroadcastReceiver,
30 | BroadcastReceiver queueActivityClosedBroadcastReceiver,
31 | BroadcastReceiver queueUserExitedBroadcastReceiver,
32 | BroadcastReceiver queueErrorBroadcastReceiver,
33 | BroadcastReceiver webViewClosedBroadcastReceiver,
34 | BroadcastReceiver onSessionRestartReceiver);
35 | }
36 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueActivityBase.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Activity;
5 | import android.net.Uri;
6 | import android.net.http.SslError;
7 | import android.os.Build;
8 | import android.os.Bundle;
9 | import android.util.Log;
10 | import android.view.View;
11 | import android.webkit.CookieSyncManager;
12 | import android.webkit.SslErrorHandler;
13 | import android.webkit.WebChromeClient;
14 | import android.webkit.WebResourceError;
15 | import android.webkit.WebResourceRequest;
16 | import android.webkit.WebResourceResponse;
17 | import android.webkit.WebView;
18 | import android.webkit.WebViewClient;
19 | import android.widget.FrameLayout;
20 | import android.widget.ProgressBar;
21 |
22 | public class QueueActivityBase {
23 | private final Activity _context;
24 | private String queueUrl;
25 | private String targetUrl;
26 | private WebView webview;
27 | private String webViewUserAgent;
28 | @SuppressLint("StaticFieldLeak")
29 | private static WebView previousWebView;
30 | private IUriOverrider uriOverrider;
31 | private final IWaitingRoomStateBroadcaster broadcaster;
32 | private QueueItEngineOptions options;
33 |
34 | public QueueActivityBase(Activity context) {
35 | _context = context;
36 | options = QueueItEngineOptions.getDefault();
37 | broadcaster = new WaitingRoomStateBroadcaster(_context);
38 | }
39 |
40 | public QueueItEngineOptions getOptions(){
41 | return options;
42 | }
43 |
44 | WebViewClient webviewClient = new WebViewClient() {
45 |
46 | @Override
47 | public void onPageFinished(WebView view, String url) {
48 | super.onPageFinished(view, url);
49 | CookieSyncManager.getInstance().sync();
50 | }
51 |
52 | @Override
53 | public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
54 | String errorMessage;
55 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
56 | errorMessage = String.format("%s %s: %s %s", request.getMethod(), request.getUrl(), errorResponse.getStatusCode(), errorResponse.getReasonPhrase());
57 | } else {
58 | errorMessage = errorResponse.toString();
59 | }
60 | Log.v("QueueActivity", String.format("%s: %s", "onReceivedHttpError", errorMessage));
61 | super.onReceivedHttpError(view, request, errorResponse);
62 | }
63 |
64 | @Override
65 | public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
66 | String errorMessage;
67 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
68 | errorMessage = String.format("%s %s: %s %s", request.getMethod(), request.getUrl(), error.getErrorCode(), error.getDescription());
69 | } else {
70 | errorMessage = error.toString();
71 | }
72 | Log.v("QueueActivity", String.format("%s: %s", "onReceivedError", errorMessage));
73 | super.onReceivedError(view, request, error);
74 | }
75 |
76 | @Override
77 | public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
78 | handler.cancel();
79 | broadcaster.broadcastQueueError("SslError, code: " + error.getPrimaryError());
80 | disposeWebview(webview);
81 | }
82 |
83 | public boolean shouldOverrideUrlLoading(WebView view, String urlString) {
84 | return uriOverrider.handleNavigationRequest(urlString, webview, new UriOverrideWrapper() {
85 |
86 | @Override
87 | protected void onQueueUrlChange(String uri) {
88 | broadcaster.broadcastChangedQueueUrl(uri);
89 | }
90 |
91 | @Override
92 | protected void onPassed(String queueItToken) {
93 | broadcaster.broadcastQueuePassed(queueItToken);
94 | disposeWebview(webview);
95 | }
96 |
97 | @Override
98 | protected void onCloseClicked() {
99 | broadcaster.broadcastWebViewClosed();
100 | disposeWebview(webview);
101 | }
102 |
103 | @Override
104 | protected void onSessionRestart() {
105 | broadcaster.broadcastOnSessionRestart();
106 | disposeWebview(webview);
107 | }
108 | });
109 | }
110 | };
111 |
112 | private static void cleanupWebView() {
113 | if (previousWebView == null) return;
114 | previousWebView.destroy();
115 | previousWebView = null;
116 | }
117 |
118 | //was onCreated
119 | public void initialize(Bundle savedInstanceState) {
120 | uriOverrider = new UriOverrider();
121 | _context.setContentView(R.layout.activity_queue);
122 | readActivityExtras(savedInstanceState);
123 | cleanupWebView();
124 | final ProgressBar progressBar = _context.findViewById(R.id.progressBar);
125 |
126 | FrameLayout layout = _context.findViewById(R.id.relativeLayout);
127 | webview = new WebView(_context);
128 | layout.addView(webview);
129 | previousWebView = webview;
130 | webview.getSettings().setJavaScriptEnabled(true);
131 | webview.setWebChromeClient(new WebChromeClient() {
132 | @Override
133 | public void onProgressChanged(WebView view, int newProgress) {
134 | Log.v("Progress", Integer.toString(newProgress));
135 | if (newProgress < 100) {
136 | progressBar.setVisibility(View.VISIBLE);
137 | } else {
138 | progressBar.setVisibility(View.GONE);
139 | }
140 | progressBar.setProgress(newProgress);
141 | super.onProgressChanged(view, newProgress);
142 | }
143 | });
144 | webview.setWebViewClient(webviewClient);
145 | Log.v("QueueITEngine", "Loading initial URL: " + queueUrl);
146 | setUserAgent(webViewUserAgent);
147 | webview.loadUrl(queueUrl);
148 | }
149 |
150 | public void saveInstanceState(Bundle outState) {
151 | outState.putString("queueUrl", queueUrl);
152 | outState.putString("targetUrl", targetUrl);
153 | outState.putString("webViewUserAgent", webViewUserAgent);
154 | outState.putString("userId", uriOverrider.getUserId());
155 | }
156 |
157 | public void destroy() {
158 | if (_context.isFinishing()) {
159 | broadcaster.broadcastQueueActivityClosed();
160 | }
161 | }
162 |
163 | private void readActivityExtras(Bundle savedInstanceState) {
164 | if (savedInstanceState == null) {
165 | Bundle extras = _context.getIntent().getExtras();
166 | if (extras == null) {
167 | queueUrl = null;
168 | targetUrl = null;
169 | webViewUserAgent = null;
170 | } else {
171 | queueUrl = extras.getString("queueUrl");
172 | targetUrl = extras.getString("targetUrl");
173 | webViewUserAgent = extras.getString("webViewUserAgent");
174 | uriOverrider.setUserId(extras.getString("userId"));
175 | options = (QueueItEngineOptions)extras.getParcelable("options");
176 | }
177 | } else {
178 | queueUrl = (String) savedInstanceState.getSerializable("queueUrl");
179 | targetUrl = (String) savedInstanceState.getSerializable("targetUrl");
180 | webViewUserAgent = (String) savedInstanceState.getSerializable("webViewUserAgent");
181 | uriOverrider.setUserId((String) savedInstanceState.getSerializable("userId"));
182 | }
183 |
184 | uriOverrider.setTarget(Uri.parse(targetUrl));
185 | uriOverrider.setQueue(Uri.parse(queueUrl));
186 | }
187 |
188 | private void disposeWebview(WebView webView) {
189 | webView.loadUrl("about:blank");
190 | _context.finish();
191 | }
192 |
193 | private void setUserAgent(String userAgent) {
194 | userAgent = (userAgent != null) ? userAgent : UserAgentManager.getUserAgent();
195 | System.setProperty("http.agent", userAgent);
196 | webview.getSettings().setUserAgentString(userAgent);
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueCache.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 |
4 | import android.content.Context;
5 | import android.content.SharedPreferences;
6 | import android.preference.PreferenceManager;
7 | import android.text.TextUtils;
8 |
9 | import java.util.Calendar;
10 |
11 | public class QueueCache {
12 |
13 | private final String _cacheKey;
14 | private static final String KEY_QUEUE_URL = "queueUrl";
15 | private static final String KEY_URL_TTL = "url_ttl";
16 | private static final String KEY_TARGET_URL = "target_url";
17 | private final Context _context;
18 |
19 | public QueueCache(Context context, String customerId, String eventOrAliasId)
20 | {
21 | _context = context;
22 | _cacheKey = "queueit_" + customerId + eventOrAliasId;
23 | }
24 |
25 | public boolean isEmpty()
26 | {
27 | return TextUtils.isEmpty(getQueueUrl());
28 | }
29 |
30 | public Calendar getUrlTtl()
31 | {
32 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_context);
33 | long time = sharedPreferences.getLong(_cacheKey + KEY_URL_TTL, 0);
34 | Calendar calendar = Calendar.getInstance();
35 | calendar.setTimeInMillis(time);
36 | return calendar;
37 | }
38 |
39 | public String getQueueUrl()
40 | {
41 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_context);
42 | return sharedPreferences.getString(_cacheKey + KEY_QUEUE_URL, "");
43 | }
44 |
45 | public String getTargetUrl()
46 | {
47 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_context);
48 | return sharedPreferences.getString(_cacheKey + KEY_TARGET_URL, "");
49 | }
50 |
51 | public void update(String queueUrl, int urlTTLInMinutes, String targetUrl){
52 | if (urlTTLInMinutes <= 0) {
53 | return;
54 | }
55 |
56 | Calendar queueUrlTtl = Calendar.getInstance();
57 | queueUrlTtl.add(Calendar.MINUTE, urlTTLInMinutes);
58 | update(queueUrl, queueUrlTtl, targetUrl);
59 | }
60 |
61 | public void update(String queueUrl, Calendar urlTtl, String targetUrl)
62 | {
63 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_context);
64 | SharedPreferences.Editor editor = sharedPreferences.edit();
65 | editor.putString(_cacheKey + KEY_QUEUE_URL, queueUrl);
66 | editor.putLong(_cacheKey + KEY_URL_TTL, urlTtl.getTimeInMillis());
67 | editor.putString(_cacheKey + KEY_TARGET_URL, targetUrl);
68 | editor.commit();
69 | }
70 |
71 | public void clear()
72 | {
73 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_context);
74 | SharedPreferences.Editor editor = sharedPreferences.edit();
75 | editor.remove(_cacheKey + KEY_QUEUE_URL);
76 | editor.remove(_cacheKey + KEY_URL_TTL);
77 | editor.remove(_cacheKey + KEY_TARGET_URL);
78 | editor.commit();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueDisabledInfo.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | public class QueueDisabledInfo {
4 | private final String _queueItToken;
5 |
6 | public QueueDisabledInfo(String queueItToken) {
7 | _queueItToken = queueItToken;
8 | }
9 |
10 | public String getQueueItToken() {
11 | return _queueItToken;
12 | }
13 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueITApiClient.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.content.Context;
4 | import android.os.Handler;
5 | import android.text.TextUtils;
6 | import android.util.Log;
7 |
8 | import org.json.JSONException;
9 | import org.json.JSONObject;
10 |
11 | import java.io.IOException;
12 | import java.net.URL;
13 | import java.text.DateFormat;
14 | import java.text.SimpleDateFormat;
15 | import java.util.Calendar;
16 | import java.util.Date;
17 | import java.util.Locale;
18 | import java.util.TimeZone;
19 |
20 | import okhttp3.Call;
21 | import okhttp3.Callback;
22 | import okhttp3.HttpUrl;
23 | import okhttp3.MediaType;
24 | import okhttp3.OkHttpClient;
25 | import okhttp3.Request;
26 | import okhttp3.RequestBody;
27 | import okhttp3.Response;
28 |
29 | public class QueueITApiClient {
30 |
31 | private final String _customerId;
32 | private final String _eventOrAliasId;
33 | private final String _userId;
34 | private final String _userAgent;
35 | private final String _sdkVersion;
36 | private final String _layoutName;
37 | private final String _language;
38 | private final String _enqueueToken;
39 | private final String _enqueueKey;
40 | private final QueueITApiClientListener _queueITApiClientListener;
41 |
42 | public static boolean IsTest = false;
43 |
44 | private URL getApiUrl() {
45 | String hostname = String.format(IsTest ?
46 | "%s.test.queue-it.net" :
47 | "%s.queue-it.net",
48 | _customerId);
49 |
50 | String path = String.format("api/mobileapp/queue/%s/%s/enqueue", _customerId, _eventOrAliasId);
51 | return new HttpUrl.Builder()
52 | .scheme("https")
53 | .host(hostname)
54 | .addPathSegments(path)
55 | .addQueryParameter("userId", _userId)
56 | .build().url();
57 | }
58 |
59 | public QueueITApiClient(String customerId,
60 | String eventOrAliasId,
61 | String userId,
62 | String userAgent,
63 | String sdkVersion,
64 | String layoutName,
65 | String language,
66 | String enqueueToken,
67 | String enqueueKey,
68 | QueueITApiClientListener queueITApiClientListener){
69 | _customerId = customerId;
70 | _eventOrAliasId = eventOrAliasId;
71 | _userId = userId;
72 | _userAgent = userAgent;
73 | _sdkVersion = sdkVersion;
74 | _layoutName = layoutName;
75 | _language = language;
76 | _enqueueToken = enqueueToken;
77 | _enqueueKey = enqueueKey;
78 | _queueITApiClientListener = queueITApiClientListener;
79 | }
80 |
81 | public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
82 |
83 | public void init(final Context context) {
84 | URL enqueueUrl = getApiUrl();
85 | OkHttpClient client = new OkHttpClient();
86 | String putBody = getJsonObject().toString();
87 | RequestBody body = RequestBody.create(JSON, putBody);
88 |
89 | Log.v("QueueITApiClient", "API call " + getISO8601StringForDate(Calendar.getInstance().getTime()) + ": " + enqueueUrl.toString() + ": " + putBody);
90 |
91 | Request request = new Request.Builder()
92 | .url(enqueueUrl)
93 | .post(body)
94 | .build();
95 | client.newCall(request).enqueue(new Callback() {
96 | final Handler mainHandler = new Handler(context.getMainLooper());
97 |
98 | @Override
99 | public void onFailure(Call call, IOException e) {
100 | final String message = e.toString();
101 | mainHandler.post(new Runnable() {
102 | @Override
103 | public void run() {
104 | _queueITApiClientListener.onFailure(message, 0);
105 | }
106 | });
107 | }
108 |
109 | @Override
110 | public void onResponse(Call call, Response response) throws IOException {
111 | if (!response.isSuccessful()) {
112 | final String errorMessage = String.format("%s %s", response.message(), response.body().string());
113 | final int code = response.code();
114 | mainHandler.post(new Runnable() {
115 | @Override
116 | public void run() {
117 | _queueITApiClientListener.onFailure(errorMessage, code);
118 | }
119 | });
120 | return;
121 | }
122 |
123 | final String body = response.body().string();
124 | try {
125 | JSONObject jsonObject = new JSONObject(body);
126 | final String queueId = optString(jsonObject, "QueueId");
127 | final String queueUrl = optString(jsonObject, "QueueUrl");
128 | final int queueUrlTtlInMinutes = optInt(jsonObject, "QueueUrlTTLInMinutes");
129 | final String eventTargetUrl = optString(jsonObject, "EventTargetUrl");
130 | final String queueItToken = optString(jsonObject, "QueueitToken");
131 | mainHandler.post(new Runnable() {
132 | @Override
133 | public void run() {
134 | String updatedQueueUrl = QueueUrlHelper.urlUpdateNeeded(queueUrl, _userId) ?
135 | QueueUrlHelper.updateUrl(queueUrl, _userId).toString() :
136 | queueUrl;
137 | _queueITApiClientListener.onSuccess(queueId, updatedQueueUrl, queueUrlTtlInMinutes, eventTargetUrl, queueItToken);
138 | }
139 | });
140 | } catch (JSONException e) {
141 | mainHandler.post(new Runnable() {
142 | @Override
143 | public void run() {
144 | _queueITApiClientListener.onFailure("Server did not return valid JSON: " + body, 0);
145 | }
146 | });
147 | }
148 | }
149 | });
150 | }
151 |
152 | private static String getISO8601StringForDate(Date date) {
153 | DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
154 | dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
155 | return dateFormat.format(date);
156 | }
157 |
158 | private String optString(JSONObject json, String key) {
159 | if (json.isNull(key))
160 | return null;
161 | else
162 | return json.optString(key, null);
163 | }
164 |
165 | private int optInt(JSONObject json, String key) {
166 | if (json.isNull(key))
167 | return 0;
168 | else
169 | return json.optInt(key, 0);
170 | }
171 |
172 | private JSONObject getJsonObject() {
173 | try {
174 | JSONObject jsonObject = new JSONObject();
175 | jsonObject.put("userId", _userId);
176 | jsonObject.put("userAgent", _userAgent);
177 | jsonObject.put("sdkVersion", _sdkVersion);
178 | if (!TextUtils.isEmpty(_layoutName)) {
179 | jsonObject.put("layoutName", _layoutName);
180 | }
181 | if (!TextUtils.isEmpty(_language)) {
182 | jsonObject.put("language", _language);
183 | }
184 | if(!TextUtils.isEmpty(_enqueueToken)){
185 | jsonObject.put("enqueueToken", _enqueueToken);
186 | }
187 | if(!TextUtils.isEmpty(_enqueueKey)){
188 | jsonObject.put("enqueueKey", _enqueueKey);
189 | }
190 |
191 | return jsonObject;
192 | } catch (JSONException e) {
193 | throw new RuntimeException(e);
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueITApiClientListener.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | interface QueueITApiClientListener {
4 | void onSuccess(String queueId, String queueUrlString, int queueUrlTtlInMinutes, String eventTargetUrl, String queueItToken);
5 | void onFailure(String errorMessage, int errorCode);
6 | }
7 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueITEngine.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 | import java.util.Calendar;
6 |
7 | public class QueueITEngine {
8 |
9 | private QueueITWaitingRoomProvider _queueITWaitingRoomProvider;
10 | private QueueITWaitingRoomView _queueITWaitingRoomView;
11 |
12 | private QueueListener _queueITEngineListener;
13 | private QueueTryPassResult _queueTryPassResult;
14 |
15 | public QueueITEngine(Context activityContext,
16 | String customerId,
17 | String eventOrAliasId,
18 | QueueListener queueListener) {
19 | this(activityContext,
20 | customerId,
21 | eventOrAliasId,
22 | "",
23 | "",
24 | queueListener,
25 | QueueItEngineOptions.getDefault());
26 | }
27 |
28 | public QueueITEngine(Context activityContext,
29 | String customerId,
30 | String eventOrAliasId,
31 | String layoutName,
32 | String language,
33 | QueueListener queueListener) {
34 | this(activityContext,
35 | customerId,
36 | eventOrAliasId,
37 | layoutName,
38 | language,
39 | queueListener,
40 | QueueItEngineOptions.getDefault());
41 | }
42 |
43 | public QueueITEngine(Context activityContext,
44 | String customerId,
45 | String eventOrAliasId,
46 | String layoutName,
47 | String language,
48 | QueueListener queueListener,
49 | QueueItEngineOptions options) {
50 | if (options == null) {
51 | options = QueueItEngineOptions.getDefault();
52 | }
53 | UserAgentManager.initialize(activityContext);
54 | _queueITEngineListener = queueListener;
55 |
56 | QueueListener queueITQueueListener = new QueueListener() {
57 | @Override
58 | protected void onQueuePassed(QueuePassedInfo queuePassedInfo) {
59 | _queueITEngineListener.onQueuePassed(queuePassedInfo);
60 | }
61 |
62 | @Override
63 | protected void onQueueViewWillOpen() {
64 | _queueITEngineListener.onQueueViewWillOpen();
65 | }
66 |
67 | @Override
68 | protected void onQueueDisabled(QueueDisabledInfo queueDisabledInfo) {
69 | _queueITEngineListener.onQueueDisabled(queueDisabledInfo);
70 | }
71 |
72 | @Override
73 | protected void onQueueItUnavailable() {
74 | _queueITEngineListener.onQueueItUnavailable();
75 | }
76 |
77 | @Override
78 | protected void onError(Error error, String errorMessage) {
79 | _queueITEngineListener.onError(error, errorMessage);
80 | }
81 |
82 | @Override
83 | public void onSessionRestart(QueueITEngine queueITEngine) {
84 | _queueITEngineListener.onSessionRestart(QueueITEngine.this);
85 | }
86 |
87 | @Override
88 | public void onUserExited() {
89 | _queueITEngineListener.onUserExited();
90 | }
91 |
92 | @Override
93 | public void onWebViewClosed() {
94 | _queueITEngineListener.onWebViewClosed();
95 | }
96 |
97 | @Override
98 | protected void onQueueUrlChanged(String url) {
99 | _queueITEngineListener.onQueueUrlChanged(url);
100 | }
101 | };
102 |
103 | final String webViewUserAgent = options.getWebViewUserAgent();
104 |
105 | QueueITWaitingRoomProviderListener queueITWaitingRoomProviderListener = new QueueITWaitingRoomProviderListener() {
106 | @Override
107 | public void onSuccess(QueueTryPassResult queueTryPassResult) {
108 | if (queueTryPassResult.getRedirectType() == RedirectType.safetynet){
109 | queueITQueueListener.onQueuePassed(new QueuePassedInfo(queueTryPassResult.getQueueItToken()));
110 | return;
111 | }
112 | if (queueTryPassResult.getRedirectType() == RedirectType.disabled || queueTryPassResult.getRedirectType() == RedirectType.afterevent || queueTryPassResult.getRedirectType() == RedirectType.idle) {
113 | queueITQueueListener.onQueueDisabled(new QueueDisabledInfo(queueTryPassResult.getQueueItToken()));
114 | return;
115 | }
116 |
117 | _queueTryPassResult = queueTryPassResult;
118 | _queueITWaitingRoomView.showQueue(_queueTryPassResult, webViewUserAgent);
119 | }
120 |
121 | @Override
122 | public void onFailure(String errorMessage, Error errorCode) {
123 | if (errorCode == Error.Queueit_NotAvailable){
124 | _queueITEngineListener.onQueueItUnavailable();
125 | return;
126 | }
127 | _queueITEngineListener.onError(errorCode,errorMessage);
128 | }
129 | };
130 |
131 | _queueITWaitingRoomProvider = new QueueITWaitingRoomProvider(activityContext, customerId,eventOrAliasId,layoutName,language, queueITWaitingRoomProviderListener);
132 | _queueITWaitingRoomView = new QueueITWaitingRoomView(activityContext, queueITQueueListener, options);
133 | }
134 |
135 | public void setViewDelay(int delayInterval) {
136 | _queueITWaitingRoomView.setViewDelay(delayInterval);
137 | }
138 |
139 | public boolean IsRequestInProgress() {
140 | return _queueITWaitingRoomProvider.IsRequestInProgress();
141 | }
142 |
143 |
144 |
145 | public void run(Context activityContext) throws QueueITException {
146 | _queueITWaitingRoomProvider.tryPass();
147 |
148 | }
149 |
150 |
151 | public void runWithEnqueueToken(Context activityContext, String enqueueToken)
152 | throws QueueITException {
153 | if (_queueITWaitingRoomProvider.IsRequestInProgress()){
154 | throw new QueueITException("Request is already in progress");
155 | }
156 |
157 | _queueITWaitingRoomProvider.tryPassWithEnqueueToken(enqueueToken);
158 |
159 | }
160 |
161 |
162 | public void runWithEnqueueKey(Context activityContext, String enqueueKey)
163 | throws QueueITException {
164 | if (_queueITWaitingRoomProvider.IsRequestInProgress()){
165 | throw new QueueITException("Request is already in progress");
166 | }
167 |
168 | _queueITWaitingRoomProvider.tryPassWithEnqueueKey(enqueueKey);
169 | }
170 |
171 | public String getSdkVersion() {
172 | return _queueITWaitingRoomProvider.getSdkVersion();
173 | }
174 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueITException.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | public class QueueITException extends Exception {
4 | public QueueITException(String message) {
5 | super(message);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueITWaitingRoomProviderListener.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | public interface QueueITWaitingRoomProviderListener {
4 | void onSuccess(QueueTryPassResult queueTryPassResult);
5 | void onFailure(String errorMessage, Error errorCode);
6 | }
7 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueITWaitingRoomView.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Handler;
7 | import android.provider.Settings;
8 | import android.util.Log;
9 |
10 | public class QueueITWaitingRoomView {
11 | private final IWaitingRoomStateBroadcaster _stateBroadcaster;
12 | private final QueueListener _queueListener;
13 | private final QueueItEngineOptions _options;
14 | private Context _context;
15 |
16 | private int _delayInterval = 0;
17 |
18 | public QueueITWaitingRoomView(Context activityContext,
19 | QueueListener queueListener) {
20 | this(activityContext,
21 | queueListener,
22 | QueueItEngineOptions.getDefault());
23 | }
24 |
25 | public QueueITWaitingRoomView(Context activityContext,
26 | QueueListener queueListener,
27 | QueueItEngineOptions options) {
28 | if (options == null) {
29 | options = QueueItEngineOptions.getDefault();
30 | }
31 | UserAgentManager.initialize(activityContext);
32 | _context = activityContext;
33 | _queueListener = queueListener;
34 | _stateBroadcaster = new WaitingRoomStateBroadcaster(_context);
35 | _options = options;
36 | }
37 |
38 | public void showQueue(final QueueTryPassResult queueTryPassResult, String webViewUserAgent) {
39 | if(queueTryPassResult == null){
40 | Log.e("QueueITWaitingRoomView", "queuePassedInfo parameter is empty");
41 | return;
42 | }
43 | raiseQueueViewWillOpen();
44 |
45 | Handler handler = new Handler();
46 | Runnable r = new Runnable() {
47 | public void run() {
48 | showQueuePage(queueTryPassResult.getQueueUrl(), queueTryPassResult.getTargetUrl(), webViewUserAgent);
49 | }
50 | };
51 | handler.postDelayed(r, _delayInterval);
52 | }
53 |
54 |
55 | public void setViewDelay(int delayInterval) {
56 | _delayInterval = delayInterval;
57 | }
58 |
59 |
60 | private void showQueuePage(String queueUrl, final String targetUrl, String webViewUserAgent) {
61 | _stateBroadcaster.registerReceivers(_queuePassedBroadcastReceiver,
62 | _queueUrlChangedBroadcastReceiver,
63 | _queueActivityClosedBroadcastReceiver,
64 | _queueUserExitedBroadcastReceiver,
65 | _queueErrorBroadcastReceiver,
66 | _webViewClosedBroadcastReceiver,
67 | _webViewOnSessionRestartReceiver);
68 |
69 | Intent intent = new Intent(_context, QueueActivity.class);
70 | intent.putExtra("queueUrl", queueUrl);
71 | intent.putExtra("targetUrl", targetUrl);
72 | intent.putExtra("webViewUserAgent", webViewUserAgent);
73 | intent.putExtra("userId", getUserId());
74 | intent.putExtra("options", _options);
75 | _context.startActivity(intent);
76 | }
77 |
78 | private void raiseQueueViewWillOpen() {
79 | _queueListener.onQueueViewWillOpen();
80 | }
81 |
82 | private void raiseUserExited() {
83 | _queueListener.onUserExited();
84 | }
85 |
86 | private void raiseQueuePassed(String queueItToken) {
87 | _queueListener.onQueuePassed(new QueuePassedInfo(queueItToken));
88 | }
89 |
90 | private void raiseWebViewClosed() {
91 | _queueListener.onWebViewClosed();
92 | }
93 |
94 | private void raiseOnSessionRestart() {
95 | _queueListener.onSessionRestart(null);
96 | }
97 |
98 | private void raiseQueueUrlChanged(String url) {
99 | _queueListener.onQueueUrlChanged(url);
100 | }
101 |
102 |
103 | private final BroadcastReceiver _queuePassedBroadcastReceiver = new BroadcastReceiver() {
104 | @Override
105 | public void onReceive(Context context, Intent intent) {
106 | raiseQueuePassed(intent.getStringExtra("queue-it-token"));
107 | }
108 | };
109 |
110 | private final BroadcastReceiver _queueErrorBroadcastReceiver = new BroadcastReceiver() {
111 | @Override
112 | public void onReceive(Context context, Intent intent) {
113 | _queueListener.onError(Error.SSL_ERROR, intent.getStringExtra("error-message"));
114 | }
115 | };
116 |
117 | private final BroadcastReceiver _queueUrlChangedBroadcastReceiver = new BroadcastReceiver() {
118 | @Override
119 | public void onReceive(Context context, Intent intent) {
120 | String url = intent.getExtras().getString("url");
121 | raiseQueueUrlChanged(url);
122 | }
123 | };
124 |
125 | private final BroadcastReceiver _queueUserExitedBroadcastReceiver = new BroadcastReceiver() {
126 | @Override
127 | public void onReceive(Context context, Intent intent) {
128 | raiseUserExited();
129 | }
130 | };
131 |
132 | private final BroadcastReceiver _webViewClosedBroadcastReceiver = new BroadcastReceiver() {
133 | @Override
134 | public void onReceive(Context context, Intent intent) {
135 | raiseWebViewClosed();
136 | }
137 | };
138 |
139 | private final BroadcastReceiver _webViewOnSessionRestartReceiver = new BroadcastReceiver() {
140 | @Override
141 | public void onReceive(Context context, Intent intent) {
142 | raiseOnSessionRestart();
143 | }
144 | };
145 |
146 | private final BroadcastReceiver _queueActivityClosedBroadcastReceiver = new BroadcastReceiver() {
147 | @Override
148 | public void onReceive(Context context, Intent intent) {
149 | _stateBroadcaster.unregisterReceivers(_queuePassedBroadcastReceiver, _queueUrlChangedBroadcastReceiver,
150 | _queueActivityClosedBroadcastReceiver, _queueUserExitedBroadcastReceiver,
151 | _queueErrorBroadcastReceiver, _webViewClosedBroadcastReceiver, _webViewOnSessionRestartReceiver);
152 | }
153 | };
154 |
155 |
156 | private String getUserId() {
157 | return Settings.Secure.getString(_context.getContentResolver(), Settings.Secure.ANDROID_ID);
158 | }
159 |
160 | }
161 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueItEngineOptions.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | public class QueueItEngineOptions implements Parcelable {
7 | private boolean disableBackButtonFromWR;
8 | private String webViewUserAgent;
9 |
10 | public QueueItEngineOptions() {
11 | }
12 |
13 | public QueueItEngineOptions(boolean disableBackButtonFromWR) {
14 | this.disableBackButtonFromWR = disableBackButtonFromWR;
15 | this.webViewUserAgent = "";
16 | }
17 | public QueueItEngineOptions(String webViewUserAgent) {
18 | this.disableBackButtonFromWR = true;
19 | this.webViewUserAgent = webViewUserAgent;
20 | }
21 | public QueueItEngineOptions(boolean disableBackButtonFromWR, String webViewUserAgent) {
22 | this.disableBackButtonFromWR = disableBackButtonFromWR;
23 | this.webViewUserAgent = webViewUserAgent;
24 | }
25 |
26 | protected QueueItEngineOptions(Parcel in) {
27 | disableBackButtonFromWR = in.readInt() != 0;
28 | webViewUserAgent = in.readString();
29 | }
30 |
31 | public static final Creator CREATOR = new Creator() {
32 | @Override
33 | public QueueItEngineOptions createFromParcel(Parcel in) {
34 | return new QueueItEngineOptions(in);
35 | }
36 |
37 | @Override
38 | public QueueItEngineOptions[] newArray(int size) {
39 | return new QueueItEngineOptions[size];
40 | }
41 | };
42 |
43 | public boolean isBackButtonDisabledFromWR() {
44 | return disableBackButtonFromWR;
45 | }
46 |
47 | public void setBackButtonDisabledFromWR(boolean disableBackButtonFromWR) {
48 | this.disableBackButtonFromWR = disableBackButtonFromWR;
49 | }
50 |
51 | public String getWebViewUserAgent() {
52 | return webViewUserAgent;
53 | }
54 |
55 | public void setWebViewUserAgent(String webViewUserAgent) {
56 | this.webViewUserAgent = webViewUserAgent;
57 | }
58 |
59 | public static QueueItEngineOptions getDefault() {
60 | return new QueueItEngineOptions(true, "");
61 | }
62 |
63 | @Override
64 | public int describeContents() {
65 | return 0;
66 | }
67 |
68 | @Override
69 | public void writeToParcel(Parcel dest, int flags) {
70 | dest.writeInt(this.disableBackButtonFromWR ? 1 : 0);
71 | dest.writeString(this.webViewUserAgent);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueListener.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 |
4 | public abstract class QueueListener {
5 | protected abstract void onQueuePassed(QueuePassedInfo queuePassedInfo);
6 | protected abstract void onQueueViewWillOpen();
7 | protected abstract void onQueueDisabled(QueueDisabledInfo queueDisabledInfo);
8 | protected abstract void onQueueItUnavailable();
9 | protected abstract void onError(Error error, String errorMessage);
10 |
11 | protected void onWebViewClosed(){
12 | }
13 | protected void onUserExited(){
14 | }
15 | protected void onSessionRestart(QueueITEngine queueITEngine){
16 | }
17 | protected void onQueueUrlChanged(String url){
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueuePassedInfo.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | public class QueuePassedInfo {
4 | private final String _queueItToken;
5 |
6 | public QueuePassedInfo(String queueItToken) {
7 | _queueItToken = queueItToken;
8 | }
9 |
10 | public String getQueueItToken()
11 | {
12 | return _queueItToken;
13 | }
14 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueService.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.content.Context;
4 | import android.os.Handler;
5 | import android.text.TextUtils;
6 | import android.util.Log;
7 |
8 | import org.json.JSONException;
9 | import org.json.JSONObject;
10 |
11 | import java.io.IOException;
12 | import java.net.URL;
13 | import java.text.DateFormat;
14 | import java.text.SimpleDateFormat;
15 | import java.util.Calendar;
16 | import java.util.Date;
17 | import java.util.Locale;
18 | import java.util.TimeZone;
19 |
20 | import okhttp3.Call;
21 | import okhttp3.Callback;
22 | import okhttp3.HttpUrl;
23 | import okhttp3.MediaType;
24 | import okhttp3.OkHttpClient;
25 | import okhttp3.Request;
26 | import okhttp3.RequestBody;
27 | import okhttp3.Response;
28 |
29 | public class QueueService {
30 |
31 | private final String _customerId;
32 | private final String _eventOrAliasId;
33 | private final String _userId;
34 | private final String _userAgent;
35 | private final String _sdkVersion;
36 | private final String _layoutName;
37 | private final String _language;
38 | private final String _enqueueToken;
39 | private final String _enqueueKey;
40 | private final QueueServiceListener _queueServiceListener;
41 |
42 | public static boolean IsTest = false;
43 |
44 | private URL getApiUrl() {
45 | String hostname = String.format(IsTest ?
46 | "%s.test.queue-it.net" :
47 | "%s.queue-it.net",
48 | _customerId);
49 |
50 | String path = String.format("api/mobileapp/queue/%s/%s/enqueue", _customerId, _eventOrAliasId);
51 | return new HttpUrl.Builder()
52 | .scheme("https")
53 | .host(hostname)
54 | .addPathSegments(path)
55 | .addQueryParameter("userId", _userId)
56 | .build().url();
57 | }
58 |
59 | public QueueService(String customerId,
60 | String eventOrAliasId,
61 | String userId,
62 | String userAgent,
63 | String sdkVersion,
64 | String layoutName,
65 | String language,
66 | String enqueueToken,
67 | String enqueueKey,
68 | QueueServiceListener queueServiceListener){
69 | _customerId = customerId;
70 | _eventOrAliasId = eventOrAliasId;
71 | _userId = userId;
72 | _userAgent = userAgent;
73 | _sdkVersion = sdkVersion;
74 | _layoutName = layoutName;
75 | _language = language;
76 | _enqueueToken = enqueueToken;
77 | _enqueueKey = enqueueKey;
78 | _queueServiceListener = queueServiceListener;
79 | }
80 |
81 | public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
82 |
83 | public void init(final Context context) {
84 | URL enqueueUrl = getApiUrl();
85 | OkHttpClient client = new OkHttpClient();
86 | String putBody = getJsonObject().toString();
87 | RequestBody body = RequestBody.create(JSON, putBody);
88 |
89 | Log.v("QueueService", "API call " + getISO8601StringForDate(Calendar.getInstance().getTime()) + ": " + enqueueUrl.toString() + ": " + putBody);
90 |
91 | Request request = new Request.Builder()
92 | .url(enqueueUrl)
93 | .post(body)
94 | .build();
95 | client.newCall(request).enqueue(new Callback() {
96 | final Handler mainHandler = new Handler(context.getMainLooper());
97 |
98 | @Override
99 | public void onFailure(Call call, IOException e) {
100 | final String message = e.toString();
101 | mainHandler.post(new Runnable() {
102 | @Override
103 | public void run() {
104 | _queueServiceListener.onFailure(message, 0);
105 | }
106 | });
107 | }
108 |
109 | @Override
110 | public void onResponse(Call call, Response response) throws IOException {
111 | if (!response.isSuccessful()) {
112 | final String errorMessage = String.format("%s %s", response.message(), response.body().string());
113 | final int code = response.code();
114 | mainHandler.post(new Runnable() {
115 | @Override
116 | public void run() {
117 | _queueServiceListener.onFailure(errorMessage, code);
118 | }
119 | });
120 | return;
121 | }
122 |
123 | final String body = response.body().string();
124 | try {
125 | JSONObject jsonObject = new JSONObject(body);
126 | final String queueId = optString(jsonObject, "QueueId");
127 | final String queueUrl = optString(jsonObject, "QueueUrl");
128 | final int queueUrlTtlInMinutes = optInt(jsonObject, "QueueUrlTTLInMinutes");
129 | final String eventTargetUrl = optString(jsonObject, "EventTargetUrl");
130 | final String queueItToken = optString(jsonObject, "QueueitToken");
131 | mainHandler.post(new Runnable() {
132 | @Override
133 | public void run() {
134 | String updatedQueueUrl = QueueUrlHelper.urlUpdateNeeded(queueUrl, _userId) ?
135 | QueueUrlHelper.updateUrl(queueUrl, _userId).toString() :
136 | queueUrl;
137 | _queueServiceListener.onSuccess(queueId, updatedQueueUrl, queueUrlTtlInMinutes, eventTargetUrl, queueItToken);
138 | }
139 | });
140 | } catch (JSONException e) {
141 | mainHandler.post(new Runnable() {
142 | @Override
143 | public void run() {
144 | _queueServiceListener.onFailure("Server did not return valid JSON: " + body, 0);
145 | }
146 | });
147 | }
148 | }
149 | });
150 | }
151 |
152 | private static String getISO8601StringForDate(Date date) {
153 | DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
154 | dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
155 | return dateFormat.format(date);
156 | }
157 |
158 | private String optString(JSONObject json, String key) {
159 | if (json.isNull(key))
160 | return null;
161 | else
162 | return json.optString(key, null);
163 | }
164 |
165 | private int optInt(JSONObject json, String key) {
166 | if (json.isNull(key))
167 | return 0;
168 | else
169 | return json.optInt(key, 0);
170 | }
171 |
172 | private JSONObject getJsonObject() {
173 | try {
174 | JSONObject jsonObject = new JSONObject();
175 | jsonObject.put("userId", _userId);
176 | jsonObject.put("userAgent", _userAgent);
177 | jsonObject.put("sdkVersion", _sdkVersion);
178 | if (!TextUtils.isEmpty(_layoutName)) {
179 | jsonObject.put("layoutName", _layoutName);
180 | }
181 | if (!TextUtils.isEmpty(_language)) {
182 | jsonObject.put("language", _language);
183 | }
184 | if(!TextUtils.isEmpty(_enqueueToken)){
185 | jsonObject.put("enqueueToken", _enqueueToken);
186 | }
187 | if(!TextUtils.isEmpty(_enqueueKey)){
188 | jsonObject.put("enqueueKey", _enqueueKey);
189 | }
190 |
191 | return jsonObject;
192 | } catch (JSONException e) {
193 | throw new RuntimeException(e);
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueServiceListener.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | public interface QueueServiceListener {
4 | void onSuccess(String queueId, String queueUrlString, int queueUrlTtlInMinutes, String eventTargetUrl, String queueItToken);
5 | void onFailure(String errorMessage, int errorCode);
6 | }
7 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueTryPassResult.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | public class QueueTryPassResult {
4 | private final String _queueItToken;
5 |
6 | private final boolean _isPassedThrough;
7 | private final String _queueUrl;
8 | private final String _targetUrl;
9 | private final int _urlTTLInMinutes;
10 | private final RedirectType _RedirectType;
11 |
12 | public QueueTryPassResult(String queueItToken, String queueUrl, String targetUrl, int urlTTLInMinutes, boolean isPassedThrough, RedirectType redirectType) {
13 | _queueItToken = queueItToken;
14 | _queueUrl = queueUrl;
15 | _targetUrl = targetUrl;
16 | _urlTTLInMinutes = urlTTLInMinutes;
17 | _isPassedThrough = isPassedThrough;
18 | _RedirectType = redirectType;
19 | }
20 |
21 | public String getQueueItToken()
22 | {
23 | return _queueItToken;
24 | }
25 |
26 | public String getQueueUrl() {
27 | return _queueUrl;
28 | }
29 |
30 | public String getTargetUrl() {
31 | return _targetUrl;
32 | }
33 |
34 | public int getUrlTTLInMinutes() {
35 | return _urlTTLInMinutes;
36 | }
37 |
38 | public Boolean isPassedThrough(){
39 | return _isPassedThrough;
40 | }
41 |
42 | public RedirectType getRedirectType() {
43 | return _RedirectType;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/QueueUrlHelper.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.net.Uri;
4 |
5 | import okhttp3.HttpUrl;
6 |
7 | public abstract class QueueUrlHelper {
8 |
9 | public static Uri updateUrl(String queueUrl, String userId) {
10 | try {
11 | return updateUrl(Uri.parse(queueUrl), userId);
12 | } catch (Exception ex) {
13 | ex.printStackTrace();
14 | return Uri.parse(queueUrl);
15 | }
16 | }
17 |
18 | public static Uri updateUrl(Uri queueUrl, String userId) {
19 | String encodedQuery = queueUrl.getEncodedQuery();
20 | if(encodedQuery==null){
21 | encodedQuery = "";
22 | }
23 | if(!encodedQuery.contains("userId=")){
24 | encodedQuery = "userId=" + userId + "&" + encodedQuery;
25 | }
26 | String updatedUrl = new HttpUrl.Builder()
27 | .scheme(queueUrl.getScheme())
28 | .host(queueUrl.getHost())
29 | .encodedPath(queueUrl.getPath())
30 | .encodedQuery(encodedQuery)
31 | .build()
32 | .url()
33 | .toString();
34 | return Uri.parse(updatedUrl);
35 | }
36 |
37 | public static boolean urlUpdateNeeded(String queueUrl, String userId) {
38 | if(queueUrl==null || userId==null){
39 | return false;
40 | }
41 | Uri uri = Uri.parse(queueUrl);
42 | return urlUpdateNeeded(uri, userId);
43 | }
44 |
45 | public static boolean urlUpdateNeeded(Uri queueUrl, String userId) {
46 | if (queueUrl == null) return false;
47 | String query = queueUrl.getQuery();
48 | if (query == null) query = "";
49 | String userIdQuery = String.format("userId=%s", userId);
50 | boolean containsUserId = query.startsWith(userIdQuery) || query.contains("&" + userIdQuery);
51 | return !containsUserId;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/RedirectType.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | public enum RedirectType {
4 | unknown,
5 | queue,
6 | safetynet,
7 | afterevent,
8 | disabled,
9 | directLink,
10 | idle
11 | }
12 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/UriOverrideWrapper.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | public abstract class UriOverrideWrapper{
4 | protected abstract void onQueueUrlChange(String uri);
5 | protected abstract void onPassed(String queueItToken);
6 | protected abstract void onCloseClicked();
7 | protected abstract void onSessionRestart();
8 | }
9 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/UriOverrider.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.util.Log;
6 | import android.webkit.WebView;
7 |
8 | public class UriOverrider implements IUriOverrider {
9 | private Uri queue;
10 | private Uri target;
11 | private String userId;
12 |
13 | @Override
14 | public Uri getQueue() {
15 | return queue;
16 | }
17 |
18 | @Override
19 | public void setQueue(Uri queue) {
20 | this.queue = queue;
21 | }
22 |
23 | @Override
24 | public Uri getTarget() {
25 | return target;
26 | }
27 |
28 | @Override
29 | public void setTarget(Uri target) {
30 | this.target = target;
31 | }
32 |
33 | @Override
34 | public String getUserId() {
35 | return userId;
36 | }
37 |
38 | @Override
39 | public void setUserId(String userId) {
40 | this.userId = userId;
41 | }
42 |
43 | private boolean isBlockedUri(Uri uri) {
44 | String path = uri.getPath();
45 | return path.startsWith("/what-is-this.html");
46 | }
47 |
48 | private boolean isTargetUri(Uri destinationUri) {
49 | String destinationHost = destinationUri.getHost();
50 | String destinationPath = destinationUri.getPath();
51 |
52 | String targetHost = target.getHost();
53 | String targetPath = target.getPath();
54 |
55 | return destinationHost.equalsIgnoreCase(targetHost)
56 | && destinationPath.equals(targetPath);
57 | }
58 |
59 | private boolean isQueueItUri(Uri uri) {
60 | return uri.getScheme().equals("queueit");
61 | }
62 |
63 | private boolean isCloseLink(Uri uri) {
64 | if (!isQueueItUri(uri)) {
65 | return false;
66 | }
67 | return uri.getHost().equals("close");
68 | }
69 |
70 | private boolean isSessionRestartLink(Uri uri) {
71 | if (!isQueueItUri(uri)) {
72 | return false;
73 | }
74 | return uri.getHost().equals("restartSession");
75 | }
76 |
77 | private boolean handleDeepLink(WebView webview, Uri destinationUri, UriOverrideWrapper uriOverride) {
78 | if (isCloseLink(destinationUri)) {
79 | uriOverride.onCloseClicked();
80 | return true;
81 | } else if (isSessionRestartLink(destinationUri)) {
82 | uriOverride.onSessionRestart();
83 | return true;
84 | }
85 | Intent browserIntent = new Intent(Intent.ACTION_VIEW, destinationUri);
86 | webview.getContext().startActivity(browserIntent);
87 | return true;
88 | }
89 |
90 | @Override
91 | public boolean handleNavigationRequest(final String destinationUrlStr, WebView webview, UriOverrideWrapper uriOverride) {
92 | Log.v("QueueITEngine", "URI loading: " + destinationUrlStr);
93 | Uri destinationUri = Uri.parse(destinationUrlStr);
94 | boolean isWeb = destinationUri.getScheme() != null && (destinationUri.getScheme().equals("http")
95 | || destinationUri.getScheme().equals("https"));
96 | if (!isWeb) {
97 | return handleDeepLink(webview, destinationUri, uriOverride);
98 | }
99 | if (isBlockedUri(destinationUri)) {
100 | return true;
101 | }
102 |
103 | String navigationHost = destinationUri.getHost();
104 | String queueHost = queue.getHost();
105 | boolean isQueueItUrl = navigationHost != null && queueHost != null && queueHost.equals(navigationHost);
106 | if (isQueueItUrl) {
107 | boolean needsRewrite = QueueUrlHelper.urlUpdateNeeded(destinationUri, userId);
108 | if (needsRewrite) {
109 | destinationUri = QueueUrlHelper.updateUrl(destinationUri, userId);
110 | Log.v("QueueITEngine", "URL intercepting: " + destinationUri);
111 | }
112 | uriOverride.onQueueUrlChange(destinationUri.toString());
113 | if (needsRewrite) {
114 | webview.loadUrl(destinationUri.toString());
115 | return true;
116 | }
117 | }
118 |
119 | if (isTargetUri(destinationUri)) {
120 | String queueItToken = destinationUri.getQueryParameter("queueittoken");
121 | uriOverride.onPassed(queueItToken);
122 | return true;
123 | }
124 | if (!isQueueItUrl) {
125 | Intent browserIntent = new Intent(Intent.ACTION_VIEW, destinationUri);
126 | webview.getContext().startActivity(browserIntent);
127 | return true;
128 | }
129 | return false;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/library/src/main/java/com/queue_it/androidsdk/UserAgentManager.java:
--------------------------------------------------------------------------------
1 | package com.queue_it.androidsdk;
2 |
3 | import android.content.Context;
4 | import android.webkit.WebView;
5 |
6 | public class UserAgentManager {
7 | public static final String SDKVersion = com.queue_it.androidsdk.BuildConfig.LIBRARY_PACKAGE_NAME + "@" + BuildConfig.VERSION_NAME;
8 | private static String DeviceUserAgent;
9 |
10 | private UserAgentManager(){
11 | }
12 |
13 | public static void initialize(Context context) {
14 | DeviceUserAgent = new WebView(context).getSettings().getUserAgentString();
15 | }
16 |
17 | public static String getUserAgent() {
18 | return DeviceUserAgent + " (sdk: " + SDKVersion + ")";
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/activity_queue.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
15 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Android SDK
3 | Invalid SSL certificate. Do you want to continue?
4 |
5 |
--------------------------------------------------------------------------------
/library/src/test/resources/robolectric.properties:
--------------------------------------------------------------------------------
1 | sdk=29
--------------------------------------------------------------------------------
/publish.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven-publish'
2 | apply plugin: 'signing'
3 | apply plugin: "de.marcphilipp.nexus-publish"
4 |
5 | group = groupId
6 | version = libraryVersion
7 |
8 | def pomConfig = {
9 | description libraryDescription
10 | licenses {
11 | license {
12 | name licenseName
13 | url licenseUrl
14 | }
15 | }
16 | scm {
17 | connection gitUrl
18 | developerConnection gitUrl
19 | url siteUrl
20 | }
21 | developers {
22 | developer {
23 | name "Developers"
24 | email "devs@queue-it.com"
25 | organization organization
26 | organizationUrl organizationUrl
27 | }
28 | }
29 | }
30 |
31 | afterEvaluate {
32 | configurations.all {
33 | if (it.name =~ /DebugAll(Api|Runtime)Publication$/) {
34 | components.all.withVariantsFromConfiguration(it) {
35 | skip()
36 | }
37 | }
38 | }
39 |
40 | publishing.publications {
41 | android.libraryVariants.all { variant ->
42 | if (variant.buildType.name == "debug") return // Prevents publishing debug library
43 |
44 | def flavored = !variant.flavorName.isEmpty()
45 |
46 | /**
47 | * Translates "_" in flavor names to "-" for artifactIds, because "-" in flavor name is an
48 | * illegal character, but is well used in artifactId names.
49 | */
50 | def variantArtifactId = flavored ? variant.flavorName.replace('_', '-') : project.name
51 | def publicationName = "android-webui-sdk-${variant.name.capitalize()}"
52 |
53 | def sourceDirs = variant.sourceSets.collect {
54 | it.javaDirectories // Also includes kotlin sources if any.
55 | }
56 | def sourcesJar = task("${variant.name}SourcesJar", type: Jar) {
57 | description "Puts sources for ${variant.name} in a jar."
58 | from sourceDirs
59 | classifier = 'sources'
60 | }
61 |
62 | "$publicationName"(MavenPublication) {
63 | from components."${variant.flavorName}Release"
64 | artifactId variantArtifactId
65 | group groupId
66 | version libraryVersion
67 | artifact sourcesJar
68 |
69 | pom {
70 | withXml {
71 | def root = asNode()
72 | root.appendNode("name", "${libraryName}:${variantArtifactId}")
73 | root.appendNode("url", siteUrl)
74 | root.appendNode("description", libraryDescription)
75 | root.children().last() + pomConfig
76 | root.dependencies.removeAll { dep ->
77 | dep.scope == "test"
78 | }
79 | // Create a signed pom if we need it
80 | def pomFile = file("${project.buildDir}/generated-${variant.flavorName}-pom.xml")
81 | writeTo(pomFile)
82 | signing.sign(pomFile)
83 | }
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
90 | Properties localProps = new Properties()
91 | if (rootProject.file("local.properties").exists()) {
92 | localProps.load(rootProject.file("local.properties").newDataInputStream())
93 | }
94 |
95 | //Sign all of our publications with our PGP private key
96 | signing {
97 | useInMemoryPgpKeys(PGP_KEY, PGP_PASSWORD)
98 | publishing.publications.all { publication ->
99 | sign(publication)
100 | }
101 | }
102 |
103 | nexusPublishing {
104 | repositories {
105 | sonatype {
106 | username.set(OSSRH_USERNAME)
107 | password.set(OSSRH_PASSWORD)
108 | }
109 | }
110 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':demoapp', ':library'
2 | include ':demowithprotectedapi'
3 |
--------------------------------------------------------------------------------