├── .gitignore
├── FactoryPatternDemoProject
├── .classpath
├── .project
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── android.jar
├── build.xml
├── project.properties
├── src
│ └── com
│ │ └── factorypatterndemo
│ │ ├── tests
│ │ └── GoogleKeepTest.java
│ │ └── utils
│ │ ├── ShellCommandUtils.java
│ │ ├── UiTestTextUtils.java
│ │ ├── factories
│ │ └── SystemUiDateTimeViewHelperFactory.java
│ │ ├── helpers
│ │ ├── AsusNexus72012sdk21DatePickerHelper.java
│ │ ├── AsusNexus72012sdk21SystemDateTimeViewHelper.java
│ │ ├── AsusNexus72012sdk21TimePickerHelper.java
│ │ ├── SamsungGalaxyS5sdk19DatePickerHelper.java
│ │ ├── SamsungGalaxyS5sdk19SystemDateTimeViewHelper.java
│ │ └── SamsungGalaxyS5sdk19TimePickerHelper.java
│ │ └── interfaces
│ │ └── SystemUiDateTimeViewHelperInterface.java
├── testBuildAndRun.sh
├── uiautomator.jar
└── uiautomator2junit-0.2.jar
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /FactoryPatternDemoProject/bin/
2 | /FactoryPatternDemoProject/local.properties
3 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | FactoryPatternDemoProject
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.7
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11 | org.eclipse.jdt.core.compiler.source=1.7
12 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/android.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SeaDroids/CIWorkshopProjects/7a9eedec6cb4da36391c85acfbd40379ac157d14/FactoryPatternDemoProject/android.jar
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
29 |
30 |
31 |
35 |
36 |
37 |
38 |
39 |
40 |
49 |
50 |
51 |
52 |
56 |
57 |
69 |
70 |
71 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=Google Inc.:Google APIs:21
15 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/src/com/factorypatterndemo/tests/GoogleKeepTest.java:
--------------------------------------------------------------------------------
1 | package com.factorypatterndemo.tests;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.Calendar;
6 |
7 | import android.os.Build;
8 | import android.util.Log;
9 |
10 | import com.android.uiautomator.core.UiDevice;
11 | import com.android.uiautomator.core.UiObject;
12 | import com.android.uiautomator.core.UiObjectNotFoundException;
13 | import com.android.uiautomator.core.UiSelector;
14 | import com.android.uiautomator.testrunner.UiAutomatorTestCase;
15 | import com.factorypatterndemo.utils.ShellCommandUtils;
16 | import com.factorypatterndemo.utils.UiTestTextUtils;
17 | import com.factorypatterndemo.utils.factories.SystemUiDateTimeViewHelperFactory;
18 | import com.factorypatterndemo.utils.interfaces.SystemUiDateTimeViewHelperInterface;
19 |
20 | public class GoogleKeepTest extends UiAutomatorTestCase {
21 |
22 | private static final String LOG_TAG = "";
23 | SystemUiDateTimeViewHelperInterface systemUi = null;
24 |
25 | @Override
26 | public void setUp() throws Exception {
27 | super.setUp();
28 | // press home to close any open notification shades
29 | UiDevice.getInstance().pressHome();
30 |
31 | // clear the test environment in the app to simulate fresh install
32 | ShellCommandUtils.stopGoogleKeep();
33 | ShellCommandUtils.clearGoogleKeep();
34 |
35 | // instantiate factory
36 | systemUi = SystemUiDateTimeViewHelperFactory.create();
37 | }
38 |
39 | @Override
40 | public void tearDown() throws Exception {
41 | // press home to close any open notification shades
42 | UiDevice.getInstance().pressHome();
43 |
44 | // clean up after ourselves in the app
45 | ShellCommandUtils.stopGoogleKeep();
46 | ShellCommandUtils.clearGoogleKeep();
47 |
48 | // clean up after ourselves in the system time settings
49 | systemUi.resetAutomaticDateAndTime();
50 |
51 | super.tearDown();
52 | }
53 |
54 | // For the purposes of this demo, make sure you disable sync on Google Keep for your device's
55 | // Google Account if you're going to try running the test on multiple devices simultaneously.
56 | // Google Keep syncs notes aggressively.
57 | public void testGoogleKeepNoteReminderShouldTriggerNotification() throws UiObjectNotFoundException, IOException {
58 | final String LOG_TAG = this.getClass().getSimpleName() + "Tests";
59 | UiDevice device = getUiDevice();
60 | Calendar cal = Calendar.getInstance();
61 | String testMessage = Build.DEVICE;
62 |
63 | // set system time to 1:00 pm for convenience sake
64 | Log.i(LOG_TAG, "Set system time to 1:00pm");
65 | cal.set(Calendar.HOUR_OF_DAY, 13);
66 | cal.set(Calendar.MINUTE, 0);
67 | systemUi.setSystemTime(cal);
68 |
69 | // launch app
70 | Log.i(LOG_TAG, "Launch Google Keep");
71 | ShellCommandUtils.launchGoogleKeep();
72 |
73 | // click create new note
74 | Log.i(LOG_TAG, "Create a new note");
75 | new UiObject(new UiSelector()
76 | .resourceId("com.google.android.keep:id/new_note_button")
77 | .description("New note"))
78 | .clickAndWaitForNewWindow();
79 |
80 | // set note text
81 | Log.i(LOG_TAG, "Set note text to " + testMessage);
82 | new UiObject(new UiSelector()
83 | .className(android.widget.EditText.class.getName())
84 | .resourceId("com.google.android.keep:id/add_item_text_view"))
85 | .setText(testMessage);
86 |
87 | // click "Remind me"
88 | Log.i(LOG_TAG, "Click remind me");
89 | new UiObject(new UiSelector()
90 | .resourceId("com.google.android.keep:id/reminder_header"))
91 | .click();
92 | device.pressBack();
93 |
94 | // set reminder day to today
95 | Log.i(LOG_TAG, "Set reminder to today");
96 | new UiObject(new UiSelector()
97 | .resourceId("com.google.android.keep:id/date_spinner"))
98 | .clickAndWaitForNewWindow();
99 | new UiObject(new UiSelector()
100 | .resourceId("com.google.android.keep:id/reminder_date_today"))
101 | .click();
102 |
103 | // set reminder for custom time
104 | Log.i(LOG_TAG, "Set reminder to custom time: 0115p");
105 | new UiObject(new UiSelector()
106 | .resourceId("com.google.android.keep:id/time_spinner"))
107 | .clickAndWaitForNewWindow();
108 | new UiObject(new UiSelector()
109 | .resourceId("com.google.android.keep:id/reminder_time_custom"))
110 | .clickAndWaitForNewWindow();
111 |
112 | // set reminder time for 1:15pm
113 | // here's the tricksy bit with their compatible time picker: you can send key events
114 | // despite the fact that it has no apparent accessibility affordance. Hooray for
115 | // undocumented features! So sending "0115p" is like 1:15pm in HHmmaa.
116 | UiTestTextUtils.setStringByKeyEvents("0115p");
117 |
118 | // click done
119 | Log.i(LOG_TAG, "Click done to set reminder time");
120 | new UiObject(new UiSelector()
121 | .resourceId("com.google.android.keep:id/done_button"))
122 | .clickAndWaitForNewWindow();
123 |
124 | // even though the app is supposted to automatically save the event when edits are made,
125 | // press Navigate Up to set the event
126 | Log.i(LOG_TAG, "Press up navigation to save the note");
127 | new UiObject(new UiSelector()
128 | .className(android.widget.ImageButton.class.getName())
129 | .description("Navigate up"))
130 | .clickAndWaitForNewWindow();
131 |
132 | // set system time to 15 minutes ahead today
133 | Log.i(LOG_TAG, "Set time to 1:15pm");
134 | cal.set(Calendar.MINUTE, 15);
135 | systemUi.setSystemTime(cal);
136 |
137 | // check notification shade for reminder
138 | Log.i(LOG_TAG, "Open notification shade and check for Google Keep note");
139 | device.openNotification();
140 | device.waitForIdle(25);
141 |
142 | boolean notificationExists = false;
143 | UiObject notificationTitle = null;
144 | for (int i = 0; i < 100; i++) {
145 | notificationTitle = new UiObject(new UiSelector().resourceId("android:id/title").text(testMessage));
146 | if (notificationTitle.waitForExists(5)) {
147 | notificationExists = true;
148 | break;
149 | }
150 | }
151 |
152 | assertTrue("Expected notification with title: " + testMessage, notificationExists);
153 | }
154 |
155 | @Override
156 | public void runTest() throws Throwable {
157 | try {
158 | super.runTest();
159 | } catch (Throwable t) {
160 | String path = "/sdcard/testScreenshotsDir/" + getClass().getName();
161 | String filePath = path + "." + getName() + ".png";
162 |
163 | Log.i(LOG_TAG, "Saving screenshot in file: " + filePath);
164 | getUiDevice().takeScreenshot(new File(filePath));
165 | throw t;
166 | }
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/src/com/factorypatterndemo/utils/ShellCommandUtils.java:
--------------------------------------------------------------------------------
1 | package com.factorypatterndemo.utils;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStreamReader;
6 |
7 | public class ShellCommandUtils {
8 | public static void stopGoogleKeep() {
9 | String stopCommand = "system/bin/am force-stop com.google.android.keep";
10 | executeShellCommand(stopCommand);
11 | }
12 |
13 | public static void clearGoogleKeep() {
14 | String stopCommand = "system/bin/pm clear com.google.android.keep";
15 | executeShellCommand(stopCommand);
16 | }
17 |
18 | public static void launchGoogleKeep() {
19 | String launchCommand = "/system/bin/am start -a android.intent.action.MAIN -n com.google.android.keep/.activities.BrowseActivity";
20 | executeShellCommand(launchCommand);
21 | }
22 |
23 | public static boolean executeShellCommand(String command) {
24 | try {
25 | Process process = Runtime.getRuntime().exec(command);
26 | BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
27 | int read;
28 | char[] buffer = new char[4096];
29 | StringBuffer output = new StringBuffer();
30 | while ((read = reader.read(buffer)) > 0) {
31 | output.append(buffer, 0, read);
32 | }
33 | reader.close();
34 | process.waitFor();
35 |
36 | return true;
37 |
38 | } catch (IOException e) {
39 | throw new RuntimeException(e);
40 | } catch (InterruptedException e) {
41 | throw new RuntimeException(e);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/src/com/factorypatterndemo/utils/UiTestTextUtils.java:
--------------------------------------------------------------------------------
1 | package com.factorypatterndemo.utils;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.Calendar;
5 | import java.util.Date;
6 |
7 | import android.view.KeyEvent;
8 |
9 | import com.android.uiautomator.core.UiDevice;
10 |
11 | public class UiTestTextUtils {
12 |
13 | public static void setStringByKeyEvents(CharSequence input) {
14 | int len = input.length();
15 | for (int i=0; i thisYear) {
67 | incrementYearIndex = 3;
68 | }
69 |
70 | for (int i = 0; i < yearDifferential; i++) {
71 | yearTextView.click();
72 | new UiObject(new UiSelector().resourceId("android:id/month_text_view").index(incrementYearIndex))
73 | .click();
74 | }
75 | }
76 |
77 | public static void clickOk() throws UiObjectNotFoundException {
78 | new UiObject(new UiSelector()
79 | .className(android.widget.Button.class.getName())
80 | .resourceId("android:id/button1")
81 | .textContains("OK"))
82 | .click();
83 | }
84 |
85 | public static void clickCancel() throws UiObjectNotFoundException {
86 | new UiObject(new UiSelector()
87 | .className(android.widget.Button.class.getName())
88 | .resourceId("android:id/button2")
89 | .textContains("CANCEL"))
90 | .click();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/src/com/factorypatterndemo/utils/helpers/AsusNexus72012sdk21SystemDateTimeViewHelper.java:
--------------------------------------------------------------------------------
1 | package com.factorypatterndemo.utils.helpers;
2 |
3 | import java.io.IOException;
4 | import java.util.Calendar;
5 |
6 | import com.android.uiautomator.core.UiObject;
7 | import com.android.uiautomator.core.UiObjectNotFoundException;
8 | import com.android.uiautomator.core.UiSelector;
9 | import com.factorypatterndemo.utils.ShellCommandUtils;
10 | import com.factorypatterndemo.utils.interfaces.SystemUiDateTimeViewHelperInterface;
11 |
12 | public class AsusNexus72012sdk21SystemDateTimeViewHelper implements SystemUiDateTimeViewHelperInterface {
13 |
14 | @Override
15 | public void setSystemTime(Calendar calendar) throws UiObjectNotFoundException, IOException {
16 | ShellCommandUtils
17 | .executeShellCommand("/system/bin/am start -a android.settings.DATE_SETTINGS");
18 |
19 | UiObject dateAndTimeToggle = new UiObject(new UiSelector().text("Automatic date & time"));
20 | UiObject setDate = new UiObject(new UiSelector().text("Set date"));
21 |
22 | if (!setDate.isEnabled()) {
23 | dateAndTimeToggle.click();
24 | }
25 |
26 | new UiObject(
27 | new UiSelector().text("Set time")).click();
28 |
29 | AsusNexus72012sdk21TimePickerHelper.setTime(calendar);
30 | AsusNexus72012sdk21TimePickerHelper.clickOK();
31 | }
32 |
33 | @Override
34 | public void resetAutomaticDateAndTime() throws UiObjectNotFoundException, IOException {
35 | ShellCommandUtils
36 | .executeShellCommand("/system/bin/am start -a android.settings.DATE_SETTINGS");
37 |
38 | UiObject dateAndTimeToggle = new UiObject(new UiSelector().text("Automatic date & time"));
39 | UiObject setDate = new UiObject(new UiSelector().text("Set date"));
40 |
41 | if (setDate.isEnabled()) {
42 | dateAndTimeToggle.click();
43 | }
44 | }
45 |
46 | @Override
47 | public void setSystemDate(Calendar calendar) throws UiObjectNotFoundException, IOException {
48 | ShellCommandUtils
49 | .executeShellCommand("/system/bin/am start -a android.settings.DATE_SETTINGS");
50 |
51 | UiObject dateAndTimeToggle = new UiObject(new UiSelector().text("Automatic date and time"));
52 | UiObject setDate = new UiObject(new UiSelector().text("Set date"));
53 |
54 | if (!setDate.isEnabled()) {
55 | dateAndTimeToggle.click();
56 | }
57 |
58 | new UiObject(
59 | new UiSelector().text("Set date")).click();
60 |
61 | AsusNexus72012sdk21DatePickerHelper.setDate(calendar);
62 | AsusNexus72012sdk21DatePickerHelper.clickOk();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/src/com/factorypatterndemo/utils/helpers/AsusNexus72012sdk21TimePickerHelper.java:
--------------------------------------------------------------------------------
1 | package com.factorypatterndemo.utils.helpers;
2 |
3 | import java.io.IOException;
4 | import java.util.Calendar;
5 | import java.util.Locale;
6 |
7 | import com.android.uiautomator.core.UiObject;
8 | import com.android.uiautomator.core.UiObjectNotFoundException;
9 | import com.android.uiautomator.core.UiSelector;
10 | import com.factorypatterndemo.utils.UiTestTextUtils;
11 |
12 | public class AsusNexus72012sdk21TimePickerHelper {
13 |
14 | /**
15 | * Sets system time in settings TimePicker. Currently only supports setting time using the 12H
16 | * format.
17 | *
18 | * @param calendar
19 | * @throws UiObjectNotFoundException
20 | * @throws IOException
21 | */
22 | public static void setTime(Calendar calendar) throws UiObjectNotFoundException, IOException {
23 | int hour = calendar.get(Calendar.HOUR);
24 | int minutes = calendar.get(Calendar.MINUTE);
25 |
26 | // Adjust hour and minutes
27 | sendKeyEventsForTimeHHMM(hour, minutes);
28 |
29 | // Adjust meridiem
30 | String calMer = calendar.getDisplayName(Calendar.AM_PM, Calendar.SHORT, Locale.getDefault());
31 | String tpMer = getCurrentMeridiem();
32 | if (tpMer.equalsIgnoreCase(calMer)) {
33 | return;
34 | }
35 | if (!calMer.equalsIgnoreCase("AM")) {
36 | selectPM();
37 | } else {
38 | selectAM();
39 | }
40 | }
41 |
42 | public static String getCurrentMeridiem() throws UiObjectNotFoundException {
43 | return new UiObject(new UiSelector().resourceId("android:id/ampm_label")).getText();
44 | }
45 |
46 | public static void selectPM() throws UiObjectNotFoundException {
47 | UiTestTextUtils.setStringByKeyEvents("p");
48 | }
49 |
50 | public static void selectAM() throws UiObjectNotFoundException {
51 | UiTestTextUtils.setStringByKeyEvents("a");
52 | }
53 |
54 | public static void clickOK() throws UiObjectNotFoundException {
55 | new UiObject(new UiSelector().resourceId("android:id/button1").text("OK")).click();
56 | }
57 |
58 | public static void clickCancel() throws UiObjectNotFoundException {
59 | new UiObject(new UiSelector().resourceId("android:id/button2").text("CANCEL")).click();
60 | }
61 |
62 | public static void sendKeyEventsForTimeHHMM(int hours, int minutes) throws IOException {
63 | String hh = String.format("%d", hours);
64 | if (hh.length() < 2) {
65 | hh = "0" + hh;
66 | }
67 | String mm = String.format("%d", minutes);
68 | if (mm.length() < 2) {
69 | mm = "0" + mm;
70 | }
71 | UiTestTextUtils.setStringByKeyEvents(hh + mm);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/src/com/factorypatterndemo/utils/helpers/SamsungGalaxyS5sdk19DatePickerHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.factorypatterndemo.utils.helpers;
18 |
19 | import java.util.Calendar;
20 |
21 | import com.android.uiautomator.core.UiObject;
22 | import com.android.uiautomator.core.UiObjectNotFoundException;
23 | import com.android.uiautomator.core.UiSelector;
24 |
25 | /**
26 | * Based mostly on the AOSP version of DatePickerHelper.class with changes called out in comments.
27 | */
28 | public class SamsungGalaxyS5sdk19DatePickerHelper {
29 | public static final String MONTH_ID = "android:id/month";
30 | public static final String DAY_ID = "android:id/day";
31 | public static final String YEAR_ID = "android:id/year";
32 |
33 | // Modified from AOSP because Samsung appends: ". Double tap to edit"
34 | public static String getCurrentMonth() throws UiObjectNotFoundException {
35 | String month = getNumberPickerField(MONTH_ID).getText();
36 | month = month.substring(0, month.indexOf("."));
37 | return month;
38 | }
39 |
40 | // Modified from AOSP because Samsung appends: ". Double tap to edit"
41 | public static String getCurrentDay() throws UiObjectNotFoundException {
42 | String day = getNumberPickerField(DAY_ID).getText();
43 | day = day.substring(0, day.indexOf("."));
44 | return day;
45 | }
46 |
47 | // Modified from AOSP because Samsung appends: ". Double tap to edit"
48 | public static String getCurrentYear() throws UiObjectNotFoundException {
49 | String year = getNumberPickerField(YEAR_ID).getText();
50 | year = year.substring(0, year.indexOf("."));
51 | return year;
52 | }
53 |
54 | public static void incrementMonth() throws UiObjectNotFoundException {
55 | incrementMonth(1);
56 | }
57 |
58 | public static void incrementMonth(int count) throws UiObjectNotFoundException {
59 | for (int x = 0; x < count; x++)
60 | getNumberPickerIncrementButton(MONTH_ID).click();
61 | }
62 |
63 | public static void decrementMonth() throws UiObjectNotFoundException {
64 | decrementMonth(1);
65 | }
66 |
67 | public static void decrementMonth(int count) throws UiObjectNotFoundException {
68 | for (int x = 0; x < count; x++)
69 | getNumberPickerDecrementButton(MONTH_ID).click();
70 | }
71 |
72 | public static void incrementDay() throws UiObjectNotFoundException {
73 | incrementDay(1);
74 | }
75 |
76 | public static void incrementDay(int count) throws UiObjectNotFoundException {
77 | for (int x = 0; x < count; x++)
78 | getNumberPickerIncrementButton(DAY_ID).click();
79 | }
80 |
81 | public static void decrementDay() throws UiObjectNotFoundException {
82 | decrementDay(1);
83 | }
84 |
85 | public static void decrementDay(int count) throws UiObjectNotFoundException {
86 | for (int x = 0; x < count; x++)
87 | getNumberPickerDecrementButton(DAY_ID).click();
88 | }
89 |
90 | public static void incrementYear() throws UiObjectNotFoundException {
91 | incrementYear(1);
92 | }
93 |
94 | public static void incrementYear(int count) throws UiObjectNotFoundException {
95 | for (int x = 0; x < count; x++)
96 | getNumberPickerIncrementButton(YEAR_ID).click();
97 | }
98 |
99 | public static void decrementYear() throws UiObjectNotFoundException {
100 | decrementYear(1);
101 | }
102 |
103 | public static void decrementYear(int count) throws UiObjectNotFoundException {
104 | for (int x = 0; x < count; x++)
105 | getNumberPickerDecrementButton(YEAR_ID).click();
106 | }
107 |
108 | public static UiObject getNumberPicker(String layoutId) {
109 | return new UiObject(new UiSelector().className(
110 | android.widget.LinearLayout.class.getName()).resourceId(layoutId));
111 | }
112 |
113 | public static UiObject getNumberPickerField(String layoutId)
114 | throws UiObjectNotFoundException {
115 | return getNumberPicker(layoutId).getChild(
116 | new UiSelector().className(android.widget.EditText.class.getName()));
117 | }
118 |
119 | // Modified from AOSP because Samsung's dialog uses ImageButtons instead of Buttons.
120 | public static UiObject getNumberPickerDecrementButton(String layoutId)
121 | throws UiObjectNotFoundException {
122 | return getNumberPicker(layoutId).getChild(
123 | new UiSelector()
124 | .className(android.widget.ImageButton.class.getName())
125 | .resourceId("android:id/decrement"));
126 | }
127 |
128 | // Modified from AOSP because Samsung's dialog uses ImageButtons instead of Buttons.
129 | public static UiObject getNumberPickerIncrementButton(String layoutId)
130 | throws UiObjectNotFoundException {
131 | return getNumberPicker(layoutId).getChild(
132 | new UiSelector()
133 | .className(android.widget.ImageButton.class.getName())
134 | .resourceId("android:id/increment"));
135 | }
136 |
137 | // Modified from AOSP because Samsung's dialog instead uses Cancel and Set Buttons
138 | public static void clickSet() throws UiObjectNotFoundException {
139 | new UiObject(new UiSelector().text("Set")).click();
140 | }
141 |
142 | // Modified from AOSP because the original order in which these fields were set resulted in bugs
143 | // when run in TouchWiz
144 | public static void setDate(Calendar cal) throws UiObjectNotFoundException {
145 | int calYear = cal.get(Calendar.YEAR);
146 | int calMonth = cal.get(Calendar.MONTH);
147 | int calDay = cal.get(Calendar.DAY_OF_MONTH);
148 | // Adjust year
149 | int dpYear = Integer.parseInt(getCurrentYear());
150 | if (calYear > dpYear) {
151 | incrementYear(calYear - dpYear);
152 | } else if (dpYear > calYear) {
153 | decrementYear(dpYear - calYear);
154 | }
155 | // Adjust month - increment or decrement using the shortest path
156 | int dpMonth = toMonthNumber(getCurrentMonth());
157 | if (calMonth > dpMonth) {
158 | if (calMonth - dpMonth < 6)
159 | incrementMonth(calMonth - dpMonth);
160 | else
161 | decrementMonth(dpMonth - calMonth + 12);
162 | } else if (dpMonth > calMonth) {
163 | if (dpMonth - calMonth < 6)
164 | decrementMonth(dpMonth - calMonth);
165 | else
166 | incrementMonth(calMonth - dpMonth + 12);
167 | }
168 | // Adjust day - increment or decrement using the shortest path
169 | // while accounting for number of days in month and considering
170 | // special case for Feb and leap years.
171 | int dpDay = Integer.parseInt(getCurrentDay());
172 | if (calDay > dpDay) {
173 | if (calDay - dpDay < getDaysInMonth(calYear, calMonth) / 2)
174 | incrementDay(calDay - dpDay);
175 | else
176 | decrementDay(dpDay - calDay + getDaysInMonth(calYear, calMonth));
177 | } else if (dpDay > calDay) {
178 | if (dpDay - calDay < getDaysInMonth(calYear, calMonth) / 2)
179 | decrementDay(dpDay - calDay);
180 | else
181 | incrementDay(calDay - dpDay + getDaysInMonth(calYear, calMonth));
182 | }
183 | }
184 |
185 | private static int toMonthNumber(String monthName) {
186 | String months[] = new String[] {"January", "February", "March", "April", "May", "June",
187 | "July", "August", "September", "October", "November", "December" };
188 | for (int x = 0; x < months.length; x++) {
189 | if (months[x].contains(monthName))
190 | return x;
191 | }
192 | return 0;
193 | }
194 |
195 | /**
196 | * Get the number of days in the month
197 | *
198 | * @param year
199 | * @param month
200 | * @return
201 | */
202 | private static int getDaysInMonth(int year, int month) {
203 | Calendar cal = Calendar.getInstance();
204 | cal.set(Calendar.YEAR, year);
205 | cal.set(Calendar.MONTH, month);
206 | return cal.getActualMaximum(Calendar.DAY_OF_MONTH);
207 | }
208 | }
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/src/com/factorypatterndemo/utils/helpers/SamsungGalaxyS5sdk19SystemDateTimeViewHelper.java:
--------------------------------------------------------------------------------
1 | package com.factorypatterndemo.utils.helpers;
2 |
3 | import java.io.IOException;
4 | import java.util.Calendar;
5 |
6 | import com.android.uiautomator.core.UiObject;
7 | import com.android.uiautomator.core.UiObjectNotFoundException;
8 | import com.android.uiautomator.core.UiSelector;
9 | import com.factorypatterndemo.utils.ShellCommandUtils;
10 | import com.factorypatterndemo.utils.interfaces.SystemUiDateTimeViewHelperInterface;
11 |
12 | public class SamsungGalaxyS5sdk19SystemDateTimeViewHelper implements SystemUiDateTimeViewHelperInterface {
13 |
14 | @Override
15 | public void setSystemTime(Calendar calendar) throws UiObjectNotFoundException, IOException {
16 | ShellCommandUtils
17 | .executeShellCommand("/system/bin/am start -a android.settings.DATE_SETTINGS");
18 |
19 | UiObject dateAndTimeToggle = new UiObject(new UiSelector().text("Automatic date and time"));
20 | UiObject setDate = new UiObject(new UiSelector().text("Set date"));
21 |
22 | if (!setDate.isEnabled()) {
23 | dateAndTimeToggle.click();
24 | }
25 |
26 | new UiObject(
27 | new UiSelector().text("Set time")).click();
28 |
29 | SamsungGalaxyS5sdk19TimePickerHelper.setTime(calendar);
30 | SamsungGalaxyS5sdk19TimePickerHelper.clickSet();
31 | }
32 |
33 | @Override
34 | public void resetAutomaticDateAndTime() throws UiObjectNotFoundException, IOException {
35 | ShellCommandUtils
36 | .executeShellCommand("/system/bin/am start -a android.settings.DATE_SETTINGS");
37 |
38 | UiObject dateAndTimeToggle = new UiObject(new UiSelector().text("Automatic date and time"));
39 | UiObject setDate = new UiObject(new UiSelector().text("Set date"));
40 |
41 | if (setDate.isEnabled()) {
42 | dateAndTimeToggle.click();
43 | }
44 | }
45 |
46 | @Override
47 | public void setSystemDate(Calendar calendar) throws UiObjectNotFoundException, IOException {
48 | ShellCommandUtils
49 | .executeShellCommand("/system/bin/am start -a android.settings.DATE_SETTINGS");
50 |
51 | UiObject dateAndTimeToggle = new UiObject(new UiSelector().text("Automatic date and time"));
52 | UiObject setDate = new UiObject(new UiSelector().text("Set date"));
53 |
54 | if (!setDate.isEnabled()) {
55 | dateAndTimeToggle.click();
56 | }
57 |
58 | new UiObject(
59 | new UiSelector().text("Set date")).click();
60 |
61 | SamsungGalaxyS5sdk19DatePickerHelper.setDate(calendar);
62 | SamsungGalaxyS5sdk19DatePickerHelper.clickSet();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/src/com/factorypatterndemo/utils/helpers/SamsungGalaxyS5sdk19TimePickerHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.factorypatterndemo.utils.helpers;
18 |
19 | import java.util.Calendar;
20 | import java.util.Locale;
21 |
22 | import com.android.uiautomator.core.UiObject;
23 | import com.android.uiautomator.core.UiObjectNotFoundException;
24 | import com.android.uiautomator.core.UiSelector;
25 |
26 | /**
27 | * Based mostly on the AOSP version of TimePickerHelper.class with changes called out in comments.
28 | */
29 | public class SamsungGalaxyS5sdk19TimePickerHelper {
30 | private static final String HOUR_ID = "android:id/hour";
31 | private static final String MINUTE_ID = "android:id/minute";
32 | private static final String MERIDIEM_ID = "android:id/amPm";
33 |
34 | // Modified from AOSP because Samsung appends: ". Double tap to edit"
35 | public static String getCurrentHour() throws UiObjectNotFoundException {
36 | String hourText = getNumberPickerField(HOUR_ID).getText();
37 | hourText = hourText.substring(0, hourText.indexOf('.'));
38 | return hourText;
39 | }
40 |
41 | // Modified from AOSP because Samsung appends: ". Double tap to edit"
42 | public static String getCurrentMinute() throws UiObjectNotFoundException {
43 | String minuteText = getNumberPickerField(MINUTE_ID).getText();
44 | minuteText = minuteText.substring(0, minuteText.indexOf('.'));
45 | return minuteText;
46 | }
47 |
48 | // Modified from AOSP because Samsung doesn't use a NumberPicker for the meridiem
49 | public static String getCurrentMeridiem() throws UiObjectNotFoundException {
50 | UiObject meridiem = new UiObject(new UiSelector().className(
51 | android.widget.Button.class.getName()).resourceId(MERIDIEM_ID));
52 |
53 | return meridiem.getText();
54 | }
55 |
56 | public static void incrementHour() throws UiObjectNotFoundException {
57 | incrementHour(1);
58 | }
59 |
60 | public static void incrementHour(int count) throws UiObjectNotFoundException {
61 | for (int x = 0; x < count; x++) {
62 | getNumberPickerIncrementButton(HOUR_ID).click();
63 | }
64 | }
65 |
66 | public static void decrementHour() throws UiObjectNotFoundException {
67 | decrementHour(1);
68 | }
69 |
70 | public static void decrementHour(int count) throws UiObjectNotFoundException {
71 | for (int x = 0; x < count; x++) {
72 | getNumberPickerDecrementButton(HOUR_ID).click();
73 | }
74 | }
75 |
76 | public static void incrementMinute() throws UiObjectNotFoundException {
77 | incrementMinute(1);
78 | }
79 |
80 | public static void incrementMinute(int count) throws UiObjectNotFoundException {
81 | for (int x = 0; x < count; x++) {
82 | getNumberPickerIncrementButton(MINUTE_ID).click();
83 | }
84 | }
85 |
86 | public static void decrementMinute() throws UiObjectNotFoundException {
87 | decrementMinute(1);
88 | }
89 |
90 | public static void decrementMinute(int count) throws UiObjectNotFoundException {
91 | for (int x = 0; x < count; x++) {
92 | getNumberPickerDecrementButton(MINUTE_ID).click();
93 | }
94 | }
95 |
96 | // Modified from AOSP because Samsung doesn't use a number picker for this field
97 | public static void selectPM() throws UiObjectNotFoundException {
98 | UiObject meridiem = new UiObject(new UiSelector().className(
99 | android.widget.Button.class.getName()).resourceId(MERIDIEM_ID));
100 | if (meridiem.getText().equals("AM")) {
101 | meridiem.click();
102 | }
103 | }
104 |
105 | // Modified from AOSP because Samsung doesn't use a number picker for this field
106 | public static void selectAM() throws UiObjectNotFoundException {
107 | UiObject meridiem = new UiObject(new UiSelector().className(
108 | android.widget.Button.class.getName()).resourceId(MERIDIEM_ID));
109 | if (meridiem.getText().equals("PM")) {
110 | meridiem.click();
111 | }
112 | }
113 |
114 | public static UiObject getNumberPicker(String layoutId) {
115 | return new UiObject(new UiSelector().className(
116 | android.widget.LinearLayout.class.getName()).resourceId(layoutId));
117 | }
118 |
119 | public static UiObject getNumberPickerField(String layoutId)
120 | throws UiObjectNotFoundException {
121 | return getNumberPicker(layoutId).getChild(
122 | new UiSelector().className(android.widget.EditText.class.getName()));
123 | }
124 |
125 | // Modified from AOSP because Samsung's dialog uses ImageButtons instead of Buttons.
126 | public static UiObject getNumberPickerDecrementButton(String layoutId)
127 | throws UiObjectNotFoundException {
128 | return getNumberPicker(layoutId).getChild(
129 | new UiSelector()
130 | .className(android.widget.ImageButton.class.getName())
131 | .resourceId("android:id/decrement"));
132 | }
133 |
134 | // Modified from AOSP because Samsung's dialog uses ImageButtons instead of Buttons.
135 | public static UiObject getNumberPickerIncrementButton(String layoutId)
136 | throws UiObjectNotFoundException {
137 | return getNumberPicker(layoutId).getChild(
138 | new UiSelector()
139 | .className(android.widget.ImageButton.class.getName())
140 | .resourceId("android:id/increment"));
141 | }
142 |
143 | // Modified from AOSP because Samsung's dialog instead uses Cancel and Set Buttons
144 | public static void clickSet() throws UiObjectNotFoundException {
145 | new UiObject(new UiSelector().text("Set")).click();
146 | }
147 |
148 | public static void setTime(Calendar cal) throws UiObjectNotFoundException {
149 | // Adjust minutes - increment or decrement using the shortest path
150 | int tpMinute = Integer.parseInt(getCurrentMinute());
151 | int calMinute = cal.get(Calendar.MINUTE);
152 | if (calMinute > tpMinute) {
153 | if (calMinute - tpMinute < 30) {
154 | incrementMinute(calMinute - tpMinute);
155 | } else {
156 | decrementMinute(tpMinute - calMinute + 60);
157 | }
158 | } else if (tpMinute > calMinute) {
159 | if (tpMinute - calMinute < 30) {
160 | decrementMinute(tpMinute - calMinute);
161 | } else {
162 | incrementMinute(calMinute - tpMinute + 60);
163 | }
164 | }
165 | // Adjust hour - increment or decrement using the shortest path
166 | int tpHour = Integer.parseInt(getCurrentHour());
167 | int calHour = cal.get(Calendar.HOUR);
168 | if (calHour > tpHour) {
169 | if (calHour - tpHour < 6) {
170 | incrementHour(calHour - tpHour);
171 | } else {
172 | decrementHour(tpHour - calHour + 12);
173 | }
174 | } else if (tpHour > calHour) {
175 | if (tpHour - calHour < 6) {
176 | decrementHour(tpHour - calHour);
177 | } else {
178 | incrementHour(calHour - tpHour + 12);
179 | }
180 | }
181 | // Adjust meridiem
182 | String calMer = cal.getDisplayName(Calendar.AM_PM, Calendar.SHORT, Locale.US);
183 | String tpMer = getCurrentMeridiem();
184 | if (tpMer.equalsIgnoreCase(calMer)) {
185 | return;
186 | }
187 | if (!calMer.equalsIgnoreCase("AM")) {
188 | selectPM();
189 | } else {
190 | selectAM();
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/src/com/factorypatterndemo/utils/interfaces/SystemUiDateTimeViewHelperInterface.java:
--------------------------------------------------------------------------------
1 | package com.factorypatterndemo.utils.interfaces;
2 |
3 | import java.io.IOException;
4 | import java.util.Calendar;
5 |
6 | import com.android.uiautomator.core.UiObjectNotFoundException;
7 |
8 | public interface SystemUiDateTimeViewHelperInterface {
9 |
10 | void setSystemTime(Calendar calendar) throws UiObjectNotFoundException, IOException;
11 |
12 | void resetAutomaticDateAndTime() throws UiObjectNotFoundException, IOException;
13 |
14 | void setSystemDate(Calendar calendar) throws UiObjectNotFoundException, IOException;
15 | }
16 |
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/testBuildAndRun.sh:
--------------------------------------------------------------------------------
1 | ant -f ./build.xml clean build
2 |
3 | adb devices
4 |
5 | DEVICE_SERIAL="015d1689510bfa13" #asus nexus 7 2012
6 | #DEVICE_SERIAL="c43ed3b1" #samsung galaxy S5
7 |
8 | adb -s $DEVICE_SERIAL push ./bin/Demo.jar /sdcard/
9 |
10 | adb -s $DEVICE_SERIAL shell uiautomator runtest /sdcard/Demo.jar
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/uiautomator.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SeaDroids/CIWorkshopProjects/7a9eedec6cb4da36391c85acfbd40379ac157d14/FactoryPatternDemoProject/uiautomator.jar
--------------------------------------------------------------------------------
/FactoryPatternDemoProject/uiautomator2junit-0.2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SeaDroids/CIWorkshopProjects/7a9eedec6cb4da36391c85acfbd40379ac157d14/FactoryPatternDemoProject/uiautomator2junit-0.2.jar
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CIWorkshopProjects
2 | Example projects demonstrating various Android automation frameworks suitable to be run via continuous integration.
3 |
--------------------------------------------------------------------------------