Merge Android Pie into master
Bug: 112104996 Change-Id: I127e4e586242f7bb6011cf96a81ea87c2de6acdf
This commit is contained in:
commit
da231a3b6a
4
OWNERS
Normal file
4
OWNERS
Normal file
|
@ -0,0 +1,4 @@
|
|||
russellbrenner@google.com
|
||||
ajayns@google.com
|
||||
iofir@google.com
|
||||
yukl@google.com
|
|
@ -15,6 +15,7 @@ LOCAL_RESOURCE_DIR := \
|
|||
$(LOCAL_PATH)/platform/res
|
||||
LOCAL_SDK_VERSION := current
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, main/src platform/src)
|
||||
LOCAL_MIN_SDK_VERSION := 23
|
||||
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
|
||||
|
@ -48,4 +49,6 @@ LOCAL_SHARED_ANDROID_LIBRARIES := \
|
|||
android-support-v7-appcompat \
|
||||
android-support-v7-recyclerview
|
||||
|
||||
LOCAL_MIN_SDK_VERSION := 14
|
||||
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# DEPRECATED: This variant is no longer maintained. Use common-gingerbread instead
|
||||
#
|
||||
# Include this make file to build your application against this module.
|
||||
#
|
||||
|
@ -9,7 +10,7 @@
|
|||
# LOCAL_RESOURCE_DIR := \
|
||||
# $(LOCAL_PATH)/res
|
||||
#
|
||||
# include frameworks/opt/setupwizard/library/common.mk
|
||||
# include frameworks/opt/setupwizard/library/common-platform-deprecated.mk
|
||||
#
|
||||
|
||||
# Path to directory of setup wizard lib (e.g. frameworks/opt/setupwizard/library)
|
|
@ -20,7 +20,6 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:colorBackground"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:orientation="horizontal"
|
||||
android:tag="noBackground">
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
style="@style/SuwItemContainer.Verbose"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<FrameLayout
|
||||
|
|
30
library/gingerbread/res/values-v27/styles.xml
Normal file
30
library/gingerbread/res/values-v27/styles.xml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2018 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- Not needed for dark theme, as default nav bar bg color is black. We need a separate style
|
||||
override here since windowLightNavigationBar is new in v27, and these two styles need to be
|
||||
applied together as a unit. -->
|
||||
<style name="SuwThemeGlifV3.Light" parent="SuwBaseThemeGlifV3.Light">
|
||||
<item name="android:navigationBarColor">@color/suw_glif_v3_nav_bar_color_light</item>
|
||||
<!-- Ignore NewApi: For some reason lint seems to think this API is new in v28 (b/73514594) -->
|
||||
<item name="android:navigationBarDividerColor" tools:ignore="NewApi">@color/suw_glif_v3_nav_bar_divider_color_light</item>
|
||||
<!-- Ignore NewApi: For some reason lint seems to think this API is new in v28 (b/73514594) -->
|
||||
<item name="android:windowLightNavigationBar" tools:ignore="NewApi">true</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -20,6 +20,7 @@
|
|||
<!-- General styles -->
|
||||
|
||||
<style name="SuwThemeMaterial" parent="Theme.AppCompat.NoActionBar">
|
||||
<item name="android:colorBackground">@color/suw_color_background_dark</item>
|
||||
<item name="android:indeterminateTint" tools:ignore="NewApi">@color/suw_progress_bar_color_dark</item>
|
||||
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
|
||||
<item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
|
||||
|
@ -38,6 +39,8 @@
|
|||
<item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
|
||||
<item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item>
|
||||
<item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
|
||||
<item name="suwButtonAllCaps">true</item>
|
||||
<item name="suwButtonFontFamily">sans-serif</item>
|
||||
<item name="suwCardBackground">@drawable/suw_card_bg_dark</item>
|
||||
<item name="suwDividerInsetEnd">0dp</item>
|
||||
<item name="suwDividerInsetStart">@dimen/suw_items_icon_divider_inset</item>
|
||||
|
@ -51,6 +54,7 @@
|
|||
</style>
|
||||
|
||||
<style name="SuwThemeMaterial.Light" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:colorBackground">@color/suw_color_background_light</item>
|
||||
<item name="android:indeterminateTint" tools:ignore="NewApi">@color/suw_progress_bar_color_light</item>
|
||||
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
|
||||
<item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
|
||||
|
@ -69,6 +73,8 @@
|
|||
<item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
|
||||
<item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item>
|
||||
<item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
|
||||
<item name="suwButtonAllCaps">true</item>
|
||||
<item name="suwButtonFontFamily">sans-serif</item>
|
||||
<item name="suwCardBackground">@drawable/suw_card_bg_light</item>
|
||||
<item name="suwDividerInsetEnd">0dp</item>
|
||||
<item name="suwDividerInsetStart">@dimen/suw_items_icon_divider_inset</item>
|
||||
|
@ -98,15 +104,19 @@
|
|||
<item name="android:windowSoftInputMode">adjustResize</item>
|
||||
|
||||
<item name="colorAccent">@color/suw_color_accent_glif_dark</item>
|
||||
<item name="colorPrimary">@color/suw_color_accent_glif_dark</item>
|
||||
<item name="colorPrimary">?attr/colorAccent</item>
|
||||
<item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item>
|
||||
<item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
|
||||
<item name="suwButtonAllCaps">true</item>
|
||||
<item name="suwButtonCornerRadius">@dimen/suw_glif_button_corner_radius</item>
|
||||
<item name="suwButtonFontFamily">sans-serif</item>
|
||||
<item name="suwColorPrimary">?attr/colorPrimary</item>
|
||||
<item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
|
||||
<item name="suwDividerInsetEnd">0dp</item>
|
||||
<item name="suwDividerInsetStart">@dimen/suw_items_glif_icon_divider_inset</item>
|
||||
<item name="suwDividerInsetStartNoIcon">@dimen/suw_items_glif_text_divider_inset</item>
|
||||
<item name="suwGlifHeaderGravity">start</item>
|
||||
<item name="suwGlifIconStyle">@style/SuwGlifIcon</item>
|
||||
<item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description.Glif</item>
|
||||
<item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle.GlifDescription</item>
|
||||
<item name="suwListItemIconColor">@color/suw_list_item_icon_color_dark</item>
|
||||
|
@ -133,15 +143,19 @@
|
|||
<item name="android:windowSoftInputMode">adjustResize</item>
|
||||
|
||||
<item name="colorAccent">@color/suw_color_accent_glif_light</item>
|
||||
<item name="colorPrimary">@color/suw_color_accent_glif_light</item>
|
||||
<item name="colorPrimary">?attr/colorAccent</item>
|
||||
<item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item>
|
||||
<item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
|
||||
<item name="suwButtonAllCaps">true</item>
|
||||
<item name="suwButtonCornerRadius">@dimen/suw_glif_button_corner_radius</item>
|
||||
<item name="suwButtonFontFamily">sans-serif</item>
|
||||
<item name="suwColorPrimary">?attr/colorPrimary</item>
|
||||
<item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
|
||||
<item name="suwDividerInsetEnd">0dp</item>
|
||||
<item name="suwDividerInsetStart">@dimen/suw_items_glif_icon_divider_inset</item>
|
||||
<item name="suwDividerInsetStartNoIcon">@dimen/suw_items_glif_text_divider_inset</item>
|
||||
<item name="suwGlifHeaderGravity">start</item>
|
||||
<item name="suwGlifIconStyle">@style/SuwGlifIcon</item>
|
||||
<item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description.Glif</item>
|
||||
<item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle.GlifDescription</item>
|
||||
<item name="suwListItemIconColor">@color/suw_list_item_icon_color_light</item>
|
||||
|
@ -151,6 +165,21 @@
|
|||
<item name="textAppearanceListItemSmall">@style/TextAppearance.SuwGlifItemSummary</item>
|
||||
</style>
|
||||
|
||||
<style name="SuwThemeGlifV3" parent="SuwThemeGlifV2">
|
||||
<item name="colorAccent">@color/suw_color_accent_glif_v3</item>
|
||||
<item name="suwButtonAllCaps">false</item>
|
||||
<item name="suwButtonCornerRadius">@dimen/suw_glif_v3_button_corner_radius</item>
|
||||
<item name="suwButtonFontFamily">@string/suwFontSecondaryMedium</item>
|
||||
</style>
|
||||
|
||||
<style name="SuwBaseThemeGlifV3.Light" parent="SuwThemeGlifV2.Light">
|
||||
<item name="colorAccent">@color/suw_color_accent_glif_v3</item>
|
||||
<item name="suwButtonAllCaps">false</item>
|
||||
<item name="suwButtonCornerRadius">@dimen/suw_glif_v3_button_corner_radius</item>
|
||||
<item name="suwButtonFontFamily">@string/suwFontSecondaryMedium</item>
|
||||
</style>
|
||||
<style name="SuwThemeGlifV3.Light" parent="SuwBaseThemeGlifV3.Light" />
|
||||
|
||||
<!-- Content styles -->
|
||||
|
||||
<style name="TextAppearance.SuwDescription" parent="TextAppearance.AppCompat.Medium">
|
||||
|
@ -197,11 +226,18 @@
|
|||
ContextThemeWrapper. These self-referencing attributes make sure this is applied as
|
||||
both to the button. -->
|
||||
<item name="android:buttonStyle">@style/SuwGlifButton.Primary</item>
|
||||
<item name="android:theme">@style/SuwGlifButton.Primary</item>
|
||||
<item name="buttonStyle">@style/SuwGlifButton.Primary</item>
|
||||
|
||||
<!-- Values used in styles -->
|
||||
<item name="android:fontFamily" tools:targetApi="jelly_bean">?attr/suwButtonFontFamily</item>
|
||||
<item name="android:paddingLeft">@dimen/suw_glif_button_padding</item>
|
||||
<item name="android:paddingRight">@dimen/suw_glif_button_padding</item>
|
||||
<item name="android:textAllCaps" tools:targetApi="ice_cream_sandwich">?attr/suwButtonAllCaps</item>
|
||||
<item name="textAllCaps">?attr/suwButtonAllCaps</item>
|
||||
|
||||
<!-- Values used in themes -->
|
||||
<item name="android:buttonCornerRadius" tools:ignore="NewApi">?attr/suwButtonCornerRadius</item>
|
||||
</style>
|
||||
|
||||
<style name="SuwGlifButton.Secondary" parent="Widget.AppCompat.Button.Borderless.Colored">
|
||||
|
@ -213,11 +249,15 @@
|
|||
<item name="buttonStyle">@style/SuwGlifButton.Secondary</item>
|
||||
|
||||
<!-- Values used in styles -->
|
||||
<item name="android:fontFamily" tools:targetApi="jelly_bean">?attr/suwButtonFontFamily</item>
|
||||
<item name="android:minWidth">0dp</item>
|
||||
<item name="android:paddingLeft">@dimen/suw_glif_button_padding</item>
|
||||
<item name="android:paddingRight">@dimen/suw_glif_button_padding</item>
|
||||
<item name="android:textAllCaps" tools:targetApi="ice_cream_sandwich">?attr/suwButtonAllCaps</item>
|
||||
<item name="textAllCaps">?attr/suwButtonAllCaps</item>
|
||||
|
||||
<!-- Values used in themes -->
|
||||
<item name="android:buttonCornerRadius" tools:ignore="NewApi">?attr/suwButtonCornerRadius</item>
|
||||
<item name="android:colorControlHighlight" tools:targetApi="lollipop">@color/suw_flat_button_highlight</item>
|
||||
<item name="colorControlHighlight">@color/suw_flat_button_highlight</item>
|
||||
</style>
|
||||
|
@ -234,6 +274,13 @@
|
|||
<item name="android:background">?attr/colorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="SuwBase.ProgressBarLarge" parent="@android:style/Widget.ProgressBar.Large" />
|
||||
|
||||
<style name="SuwFourColorIndeterminateProgressBar" parent="SuwBase.ProgressBarLarge">
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:indeterminate">true</item>
|
||||
</style>
|
||||
|
||||
<!-- Navigation bar styles -->
|
||||
|
||||
<style name="SuwNavBarButtonStyle" parent="@android:style/Widget.Button">
|
||||
|
@ -258,4 +305,8 @@
|
|||
<item name="suwNavBarButtonBackground">@drawable/suw_navbar_btn_bg_light</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="SuwAlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert" />
|
||||
|
||||
<style name="SuwAlertDialogTheme.Light" parent="Theme.AppCompat.Light.Dialog.Alert" />
|
||||
</resources>
|
||||
|
|
|
@ -138,6 +138,10 @@ public class ExpandableSwitchItem extends SwitchItem
|
|||
}
|
||||
|
||||
tintCompoundDrawables(view);
|
||||
|
||||
// Expandable switch item has focusability on the expandable layout on the left, and the
|
||||
// switch on the right, but not the item itself.
|
||||
view.setFocusable(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,8 @@ package com.android.setupwizardlib.util;
|
|||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.support.v4.view.AccessibilityDelegateCompat;
|
||||
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
|
||||
|
@ -38,12 +40,11 @@ import java.util.List;
|
|||
/**
|
||||
* An accessibility delegate that allows {@link android.text.style.ClickableSpan} to be focused and
|
||||
* clicked by accessibility services.
|
||||
* <p>
|
||||
* <strong>Note: </strong> From Android O on, there is native support for ClickableSpan
|
||||
* accessibility, so this class is not needed (and indeed has no effect.)
|
||||
* </p>
|
||||
*
|
||||
* <p />Sample usage:
|
||||
* <p><strong>Note:</strong> This class is a no-op on Android O or above since there is native
|
||||
* support for ClickableSpan accessibility.
|
||||
*
|
||||
* <p>Sample usage:
|
||||
* <pre>
|
||||
* LinkAccessibilityHelper mAccessibilityHelper;
|
||||
*
|
||||
|
@ -68,294 +69,260 @@ public class LinkAccessibilityHelper extends AccessibilityDelegateCompat {
|
|||
|
||||
private static final String TAG = "LinkAccessibilityHelper";
|
||||
|
||||
private final TextView mView;
|
||||
private final Rect mTempRect = new Rect();
|
||||
private final ExploreByTouchHelper mExploreByTouchHelper;
|
||||
private final AccessibilityDelegateCompat mDelegate;
|
||||
|
||||
public LinkAccessibilityHelper(TextView view) {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
|
||||
// Pre-O, we essentially extend ExploreByTouchHelper to expose a virtual view hierarchy
|
||||
mExploreByTouchHelper = new ExploreByTouchHelper(view) {
|
||||
@Override
|
||||
protected int getVirtualViewAt(float x, float y) {
|
||||
return LinkAccessibilityHelper.this.getVirtualViewAt(x, y);
|
||||
}
|
||||
this(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
// Platform support was added in O. This helper will be no-op
|
||||
? new AccessibilityDelegateCompat()
|
||||
// Pre-O, we extend ExploreByTouchHelper to expose a virtual view hierarchy
|
||||
: new PreOLinkAccessibilityHelper(view));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
|
||||
LinkAccessibilityHelper.this.getVisibleVirtualViews(virtualViewIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPopulateEventForVirtualView(int virtualViewId,
|
||||
AccessibilityEvent event) {
|
||||
LinkAccessibilityHelper
|
||||
.this.onPopulateEventForVirtualView(virtualViewId, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPopulateNodeForVirtualView(int virtualViewId,
|
||||
AccessibilityNodeInfoCompat infoCompat) {
|
||||
LinkAccessibilityHelper
|
||||
.this.onPopulateNodeForVirtualView(virtualViewId, infoCompat);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
|
||||
Bundle arguments) {
|
||||
return LinkAccessibilityHelper.this
|
||||
.onPerformActionForVirtualView(virtualViewId, action, arguments);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
mExploreByTouchHelper = null;
|
||||
}
|
||||
mView = view;
|
||||
@VisibleForTesting
|
||||
LinkAccessibilityHelper(@NonNull AccessibilityDelegateCompat delegate) {
|
||||
mDelegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAccessibilityEvent(View host, int eventType) {
|
||||
if (mExploreByTouchHelper != null) {
|
||||
mExploreByTouchHelper.sendAccessibilityEvent(host, eventType);
|
||||
} else {
|
||||
super.sendAccessibilityEvent(host, eventType);
|
||||
}
|
||||
mDelegate.sendAccessibilityEvent(host, eventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
|
||||
if (mExploreByTouchHelper != null) {
|
||||
mExploreByTouchHelper.sendAccessibilityEventUnchecked(host, event);
|
||||
} else {
|
||||
super.sendAccessibilityEventUnchecked(host, event);
|
||||
}
|
||||
mDelegate.sendAccessibilityEventUnchecked(host, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
|
||||
return (mExploreByTouchHelper != null)
|
||||
? mExploreByTouchHelper.dispatchPopulateAccessibilityEvent(host, event)
|
||||
: super.dispatchPopulateAccessibilityEvent(host, event);
|
||||
return mDelegate.dispatchPopulateAccessibilityEvent(host, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
|
||||
if (mExploreByTouchHelper != null) {
|
||||
mExploreByTouchHelper.onPopulateAccessibilityEvent(host, event);
|
||||
} else {
|
||||
super.onPopulateAccessibilityEvent(host, event);
|
||||
}
|
||||
mDelegate.onPopulateAccessibilityEvent(host, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
|
||||
if (mExploreByTouchHelper != null) {
|
||||
mExploreByTouchHelper.onInitializeAccessibilityEvent(host, event);
|
||||
} else {
|
||||
super.onInitializeAccessibilityEvent(host, event);
|
||||
}
|
||||
mDelegate.onInitializeAccessibilityEvent(host, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
|
||||
if (mExploreByTouchHelper != null) {
|
||||
mExploreByTouchHelper.onInitializeAccessibilityNodeInfo(host, info);
|
||||
} else {
|
||||
super.onInitializeAccessibilityNodeInfo(host, info);
|
||||
}
|
||||
mDelegate.onInitializeAccessibilityNodeInfo(host, info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
|
||||
AccessibilityEvent event) {
|
||||
return (mExploreByTouchHelper != null)
|
||||
? mExploreByTouchHelper.onRequestSendAccessibilityEvent(host, child, event)
|
||||
: super.onRequestSendAccessibilityEvent(host, child, event);
|
||||
return mDelegate.onRequestSendAccessibilityEvent(host, child, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
|
||||
return (mExploreByTouchHelper != null)
|
||||
? mExploreByTouchHelper.getAccessibilityNodeProvider(host)
|
||||
: super.getAccessibilityNodeProvider(host);
|
||||
return mDelegate.getAccessibilityNodeProvider(host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityAction(View host, int action, Bundle args) {
|
||||
return (mExploreByTouchHelper != null)
|
||||
? mExploreByTouchHelper.performAccessibilityAction(host, action, args)
|
||||
: super.performAccessibilityAction(host, action, args);
|
||||
return mDelegate.performAccessibilityAction(host, action, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegated to {@link ExploreByTouchHelper}
|
||||
* Dispatches hover event to the virtual view hierarchy. This method should be called in
|
||||
* {@link View#dispatchHoverEvent(MotionEvent)}.
|
||||
*
|
||||
* @see ExploreByTouchHelper#dispatchHoverEvent(MotionEvent)
|
||||
*/
|
||||
public final boolean dispatchHoverEvent(MotionEvent event) {
|
||||
return (mExploreByTouchHelper != null) ? mExploreByTouchHelper.dispatchHoverEvent(event)
|
||||
: false;
|
||||
return mDelegate instanceof ExploreByTouchHelper
|
||||
&& ((ExploreByTouchHelper) mDelegate).dispatchHoverEvent(event);
|
||||
}
|
||||
|
||||
protected int getVirtualViewAt(float x, float y) {
|
||||
final CharSequence text = mView.getText();
|
||||
if (text instanceof Spanned) {
|
||||
final Spanned spannedText = (Spanned) text;
|
||||
final int offset = getOffsetForPosition(mView, x, y);
|
||||
ClickableSpan[] linkSpans = spannedText.getSpans(offset, offset, ClickableSpan.class);
|
||||
if (linkSpans.length == 1) {
|
||||
ClickableSpan linkSpan = linkSpans[0];
|
||||
return spannedText.getSpanStart(linkSpan);
|
||||
@VisibleForTesting
|
||||
static class PreOLinkAccessibilityHelper extends ExploreByTouchHelper {
|
||||
|
||||
private final Rect mTempRect = new Rect();
|
||||
private final TextView mView;
|
||||
|
||||
PreOLinkAccessibilityHelper(TextView view) {
|
||||
super(view);
|
||||
mView = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVirtualViewAt(float x, float y) {
|
||||
final CharSequence text = mView.getText();
|
||||
if (text instanceof Spanned) {
|
||||
final Spanned spannedText = (Spanned) text;
|
||||
final int offset = getOffsetForPosition(mView, x, y);
|
||||
ClickableSpan[] linkSpans =
|
||||
spannedText.getSpans(offset, offset, ClickableSpan.class);
|
||||
if (linkSpans.length == 1) {
|
||||
ClickableSpan linkSpan = linkSpans[0];
|
||||
return spannedText.getSpanStart(linkSpan);
|
||||
}
|
||||
}
|
||||
return ExploreByTouchHelper.INVALID_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
|
||||
final CharSequence text = mView.getText();
|
||||
if (text instanceof Spanned) {
|
||||
final Spanned spannedText = (Spanned) text;
|
||||
ClickableSpan[] linkSpans = spannedText.getSpans(0, spannedText.length(),
|
||||
ClickableSpan.class);
|
||||
for (ClickableSpan span : linkSpans) {
|
||||
virtualViewIds.add(spannedText.getSpanStart(span));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ExploreByTouchHelper.INVALID_ID;
|
||||
}
|
||||
|
||||
protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
|
||||
final CharSequence text = mView.getText();
|
||||
if (text instanceof Spanned) {
|
||||
final Spanned spannedText = (Spanned) text;
|
||||
ClickableSpan[] linkSpans = spannedText.getSpans(0, spannedText.length(),
|
||||
ClickableSpan.class);
|
||||
for (ClickableSpan span : linkSpans) {
|
||||
virtualViewIds.add(spannedText.getSpanStart(span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
|
||||
final ClickableSpan span = getSpanForOffset(virtualViewId);
|
||||
if (span != null) {
|
||||
event.setContentDescription(getTextForSpan(span));
|
||||
} else {
|
||||
Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
|
||||
event.setContentDescription(mView.getText());
|
||||
}
|
||||
}
|
||||
|
||||
protected void onPopulateNodeForVirtualView(int virtualViewId,
|
||||
AccessibilityNodeInfoCompat info) {
|
||||
final ClickableSpan span = getSpanForOffset(virtualViewId);
|
||||
if (span != null) {
|
||||
info.setContentDescription(getTextForSpan(span));
|
||||
} else {
|
||||
Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
|
||||
info.setContentDescription(mView.getText());
|
||||
}
|
||||
info.setFocusable(true);
|
||||
info.setClickable(true);
|
||||
getBoundsForSpan(span, mTempRect);
|
||||
if (mTempRect.isEmpty()) {
|
||||
Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId);
|
||||
mTempRect.set(0, 0, 1, 1);
|
||||
}
|
||||
info.setBoundsInParent(mTempRect);
|
||||
info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
|
||||
}
|
||||
|
||||
protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
|
||||
Bundle arguments) {
|
||||
if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
|
||||
ClickableSpan span = getSpanForOffset(virtualViewId);
|
||||
@Override
|
||||
protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
|
||||
final ClickableSpan span = getSpanForOffset(virtualViewId);
|
||||
if (span != null) {
|
||||
span.onClick(mView);
|
||||
return true;
|
||||
event.setContentDescription(getTextForSpan(span));
|
||||
} else {
|
||||
Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
|
||||
event.setContentDescription(mView.getText());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private ClickableSpan getSpanForOffset(int offset) {
|
||||
CharSequence text = mView.getText();
|
||||
if (text instanceof Spanned) {
|
||||
Spanned spannedText = (Spanned) text;
|
||||
ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class);
|
||||
if (spans.length == 1) {
|
||||
return spans[0];
|
||||
@Override
|
||||
protected void onPopulateNodeForVirtualView(
|
||||
int virtualViewId,
|
||||
AccessibilityNodeInfoCompat info) {
|
||||
final ClickableSpan span = getSpanForOffset(virtualViewId);
|
||||
if (span != null) {
|
||||
info.setContentDescription(getTextForSpan(span));
|
||||
} else {
|
||||
Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
|
||||
info.setContentDescription(mView.getText());
|
||||
}
|
||||
info.setFocusable(true);
|
||||
info.setClickable(true);
|
||||
getBoundsForSpan(span, mTempRect);
|
||||
if (mTempRect.isEmpty()) {
|
||||
Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId);
|
||||
mTempRect.set(0, 0, 1, 1);
|
||||
}
|
||||
info.setBoundsInParent(mTempRect);
|
||||
info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private CharSequence getTextForSpan(ClickableSpan span) {
|
||||
CharSequence text = mView.getText();
|
||||
if (text instanceof Spanned) {
|
||||
Spanned spannedText = (Spanned) text;
|
||||
return spannedText.subSequence(spannedText.getSpanStart(span),
|
||||
spannedText.getSpanEnd(span));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
// Find the bounds of a span. If it spans multiple lines, it will only return the bounds for the
|
||||
// section on the first line.
|
||||
private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) {
|
||||
CharSequence text = mView.getText();
|
||||
outRect.setEmpty();
|
||||
if (text instanceof Spanned) {
|
||||
final Layout layout = mView.getLayout();
|
||||
if (layout != null) {
|
||||
Spanned spannedText = (Spanned) text;
|
||||
final int spanStart = spannedText.getSpanStart(span);
|
||||
final int spanEnd = spannedText.getSpanEnd(span);
|
||||
final float xStart = layout.getPrimaryHorizontal(spanStart);
|
||||
final float xEnd = layout.getPrimaryHorizontal(spanEnd);
|
||||
final int lineStart = layout.getLineForOffset(spanStart);
|
||||
final int lineEnd = layout.getLineForOffset(spanEnd);
|
||||
layout.getLineBounds(lineStart, outRect);
|
||||
if (lineEnd == lineStart) {
|
||||
// If the span is on a single line, adjust both the left and right bounds
|
||||
// so outrect is exactly bounding the span.
|
||||
outRect.left = (int) Math.min(xStart, xEnd);
|
||||
outRect.right = (int) Math.max(xStart, xEnd);
|
||||
@Override
|
||||
protected boolean onPerformActionForVirtualView(
|
||||
int virtualViewId,
|
||||
int action,
|
||||
Bundle arguments) {
|
||||
if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
|
||||
ClickableSpan span = getSpanForOffset(virtualViewId);
|
||||
if (span != null) {
|
||||
span.onClick(mView);
|
||||
return true;
|
||||
} else {
|
||||
// If the span wraps across multiple lines, only use the first line (as returned
|
||||
// by layout.getLineBounds above), and adjust the "start" of outrect to where
|
||||
// the span starts, leaving the "end" of outrect at the end of the line.
|
||||
// ("start" being left for LTR, and right for RTL)
|
||||
if (layout.getParagraphDirection(lineStart) == Layout.DIR_RIGHT_TO_LEFT) {
|
||||
outRect.right = (int) xStart;
|
||||
} else {
|
||||
outRect.left = (int) xStart;
|
||||
}
|
||||
Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
|
||||
}
|
||||
|
||||
// Offset for padding
|
||||
outRect.offset(mView.getTotalPaddingLeft(), mView.getTotalPaddingTop());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return outRect;
|
||||
}
|
||||
|
||||
// Compat implementation of TextView#getOffsetForPosition().
|
||||
private ClickableSpan getSpanForOffset(int offset) {
|
||||
CharSequence text = mView.getText();
|
||||
if (text instanceof Spanned) {
|
||||
Spanned spannedText = (Spanned) text;
|
||||
ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class);
|
||||
if (spans.length == 1) {
|
||||
return spans[0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int getOffsetForPosition(TextView view, float x, float y) {
|
||||
if (view.getLayout() == null) return -1;
|
||||
final int line = getLineAtCoordinate(view, y);
|
||||
return getOffsetAtCoordinate(view, line, x);
|
||||
}
|
||||
private CharSequence getTextForSpan(ClickableSpan span) {
|
||||
CharSequence text = mView.getText();
|
||||
if (text instanceof Spanned) {
|
||||
Spanned spannedText = (Spanned) text;
|
||||
return spannedText.subSequence(
|
||||
spannedText.getSpanStart(span),
|
||||
spannedText.getSpanEnd(span));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private static float convertToLocalHorizontalCoordinate(TextView view, float x) {
|
||||
x -= view.getTotalPaddingLeft();
|
||||
// Clamp the position to inside of the view.
|
||||
x = Math.max(0.0f, x);
|
||||
x = Math.min(view.getWidth() - view.getTotalPaddingRight() - 1, x);
|
||||
x += view.getScrollX();
|
||||
return x;
|
||||
}
|
||||
// Find the bounds of a span. If it spans multiple lines, it will only return the bounds for
|
||||
// the section on the first line.
|
||||
private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) {
|
||||
CharSequence text = mView.getText();
|
||||
outRect.setEmpty();
|
||||
if (text instanceof Spanned) {
|
||||
final Layout layout = mView.getLayout();
|
||||
if (layout != null) {
|
||||
Spanned spannedText = (Spanned) text;
|
||||
final int spanStart = spannedText.getSpanStart(span);
|
||||
final int spanEnd = spannedText.getSpanEnd(span);
|
||||
final float xStart = layout.getPrimaryHorizontal(spanStart);
|
||||
final float xEnd = layout.getPrimaryHorizontal(spanEnd);
|
||||
final int lineStart = layout.getLineForOffset(spanStart);
|
||||
final int lineEnd = layout.getLineForOffset(spanEnd);
|
||||
layout.getLineBounds(lineStart, outRect);
|
||||
if (lineEnd == lineStart) {
|
||||
// If the span is on a single line, adjust both the left and right bounds
|
||||
// so outrect is exactly bounding the span.
|
||||
outRect.left = (int) Math.min(xStart, xEnd);
|
||||
outRect.right = (int) Math.max(xStart, xEnd);
|
||||
} else {
|
||||
// If the span wraps across multiple lines, only use the first line (as
|
||||
// returned by layout.getLineBounds above), and adjust the "start" of
|
||||
// outrect to where the span starts, leaving the "end" of outrect at the end
|
||||
// of the line. ("start" being left for LTR, and right for RTL)
|
||||
if (layout.getParagraphDirection(lineStart) == Layout.DIR_RIGHT_TO_LEFT) {
|
||||
outRect.right = (int) xStart;
|
||||
} else {
|
||||
outRect.left = (int) xStart;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getLineAtCoordinate(TextView view, float y) {
|
||||
y -= view.getTotalPaddingTop();
|
||||
// Clamp the position to inside of the view.
|
||||
y = Math.max(0.0f, y);
|
||||
y = Math.min(view.getHeight() - view.getTotalPaddingBottom() - 1, y);
|
||||
y += view.getScrollY();
|
||||
return view.getLayout().getLineForVertical((int) y);
|
||||
}
|
||||
// Offset for padding
|
||||
outRect.offset(mView.getTotalPaddingLeft(), mView.getTotalPaddingTop());
|
||||
}
|
||||
}
|
||||
return outRect;
|
||||
}
|
||||
|
||||
private static int getOffsetAtCoordinate(TextView view, int line, float x) {
|
||||
x = convertToLocalHorizontalCoordinate(view, x);
|
||||
return view.getLayout().getOffsetForHorizontal(line, x);
|
||||
// Compat implementation of TextView#getOffsetForPosition().
|
||||
|
||||
private static int getOffsetForPosition(TextView view, float x, float y) {
|
||||
if (view.getLayout() == null) return -1;
|
||||
final int line = getLineAtCoordinate(view, y);
|
||||
return getOffsetAtCoordinate(view, line, x);
|
||||
}
|
||||
|
||||
private static float convertToLocalHorizontalCoordinate(TextView view, float x) {
|
||||
x -= view.getTotalPaddingLeft();
|
||||
// Clamp the position to inside of the view.
|
||||
x = Math.max(0.0f, x);
|
||||
x = Math.min(view.getWidth() - view.getTotalPaddingRight() - 1, x);
|
||||
x += view.getScrollX();
|
||||
return x;
|
||||
}
|
||||
|
||||
private static int getLineAtCoordinate(TextView view, float y) {
|
||||
y -= view.getTotalPaddingTop();
|
||||
// Clamp the position to inside of the view.
|
||||
y = Math.max(0.0f, y);
|
||||
y = Math.min(view.getHeight() - view.getTotalPaddingBottom() - 1, y);
|
||||
y += view.getScrollY();
|
||||
return view.getLayout().getLineForVertical((int) y);
|
||||
}
|
||||
|
||||
private static int getOffsetAtCoordinate(TextView view, int line, float x) {
|
||||
x = convertToLocalHorizontalCoordinate(view, x);
|
||||
return view.getLayout().getOffsetForHorizontal(line, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.setupwizardlib.view;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.PorterDuff;
|
||||
|
@ -30,6 +31,7 @@ import android.widget.Button;
|
|||
* Button for navigation bar, which includes tinting of its compound drawables to be used for dark
|
||||
* and light themes.
|
||||
*/
|
||||
@SuppressLint("AppCompatCustomView")
|
||||
public class NavigationBarButton extends Button {
|
||||
|
||||
public NavigationBarButton(Context context) {
|
||||
|
|
|
@ -25,7 +25,7 @@ import android.support.v7.widget.AppCompatTextView;
|
|||
import android.text.Annotation;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.method.MovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.TextAppearanceSpan;
|
||||
import android.util.AttributeSet;
|
||||
|
@ -36,6 +36,7 @@ import com.android.setupwizardlib.span.LinkSpan;
|
|||
import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener;
|
||||
import com.android.setupwizardlib.span.SpanHelper;
|
||||
import com.android.setupwizardlib.util.LinkAccessibilityHelper;
|
||||
import com.android.setupwizardlib.view.TouchableMovementMethod.TouchableLinkMovementMethod;
|
||||
|
||||
/**
|
||||
* An extension of TextView that automatically replaces the annotation tags as specified in
|
||||
|
@ -121,7 +122,7 @@ public class RichTextView extends AppCompatTextView implements OnLinkClickListen
|
|||
// nullifying any return values of MovementMethod.onTouchEvent.
|
||||
// To still allow propagating touch events to the parent when this view doesn't have
|
||||
// links, we only set the movement method here if the text contains links.
|
||||
setMovementMethod(LinkMovementMethod.getInstance());
|
||||
setMovementMethod(TouchableLinkMovementMethod.getInstance());
|
||||
} else {
|
||||
setMovementMethod(null);
|
||||
}
|
||||
|
@ -130,6 +131,17 @@ public class RichTextView extends AppCompatTextView implements OnLinkClickListen
|
|||
// as individual TextViews consume touch events and thereby reducing the focus window
|
||||
// shown by Talkback. Disable focus if there are no links
|
||||
setFocusable(hasLinks);
|
||||
// Do not "reveal" (i.e. scroll to) this view when this view is focused. Since this view is
|
||||
// focusable in touch mode, we may be focused when the screen is first shown, and starting
|
||||
// a screen halfway scrolled down is confusing to the user.
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
|
||||
setRevealOnFocusHint(false);
|
||||
// setRevealOnFocusHint is a new API added in SDK 25. For lower SDK versions, do not
|
||||
// call setFocusableInTouchMode. We won't get touch effect on those earlier versions,
|
||||
// but the link will still work, and will prevent the scroll view from starting halfway
|
||||
// down the page.
|
||||
setFocusableInTouchMode(hasLinks);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasLinks(CharSequence text) {
|
||||
|
@ -141,6 +153,25 @@ public class RichTextView extends AppCompatTextView implements OnLinkClickListen
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("ClickableViewAccessibility") // super.onTouchEvent is called
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
// Since View#onTouchEvent always return true if the view is clickable (which is the case
|
||||
// when a TextView has a movement method), override the implementation to allow the movement
|
||||
// method, if it implements TouchableMovementMethod, to say that the touch is not handled,
|
||||
// allowing the event to bubble up to the parent view.
|
||||
boolean superResult = super.onTouchEvent(event);
|
||||
MovementMethod movementMethod = getMovementMethod();
|
||||
if (movementMethod instanceof TouchableMovementMethod) {
|
||||
TouchableMovementMethod touchableMovementMethod =
|
||||
(TouchableMovementMethod) movementMethod;
|
||||
if (touchableMovementMethod.getLastTouchEvent() == event) {
|
||||
return touchableMovementMethod.isLastTouchEventHandled();
|
||||
}
|
||||
}
|
||||
return superResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean dispatchHoverEvent(MotionEvent event) {
|
||||
if (mAccessibilityHelper != null && mAccessibilityHelper.dispatchHoverEvent(event)) {
|
||||
|
|
|
@ -18,6 +18,7 @@ package com.android.setupwizardlib.items;
|
|||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.support.annotation.StyleRes;
|
||||
import android.support.test.annotation.UiThreadTest;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.rule.UiThreadTestRule;
|
||||
|
@ -29,7 +30,6 @@ import android.widget.LinearLayout;
|
|||
import com.android.setupwizardlib.R;
|
||||
import com.android.setupwizardlib.test.util.DrawingTestHelper;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -38,40 +38,61 @@ import org.junit.runner.RunWith;
|
|||
@RunWith(AndroidJUnit4.class)
|
||||
public class ButtonItemDrawingTest {
|
||||
|
||||
private static final int GOOGLE_BLUE = 0xff4285f4;
|
||||
private static final int GLIF_ACCENT_COLOR = 0xff4285f4;
|
||||
private static final int GLIF_V3_ACCENT_COLOR = 0xff1a73e8;
|
||||
|
||||
// These tests need to be run on UI thread because button uses ValueAnimator
|
||||
@Rule
|
||||
public UiThreadTestRule mUiThreadTestRule = new UiThreadTestRule();
|
||||
|
||||
private ViewGroup mParent;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mParent = new LinearLayout(
|
||||
DrawingTestHelper.createCanvasActivity(R.style.SuwThemeGlif_Light));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testColoredButtonTheme() {
|
||||
TestButtonItem item = new TestButtonItem();
|
||||
item.setTheme(R.style.SuwButtonItem_Colored);
|
||||
item.setText("foobar");
|
||||
|
||||
final Button button = item.createButton(mParent);
|
||||
public void drawButton_glif_shouldHaveAccentColoredButton()
|
||||
throws InstantiationException, IllegalAccessException {
|
||||
Button button = createButton(R.style.SuwThemeGlif_Light);
|
||||
|
||||
DrawingTestHelper drawingTestHelper = new DrawingTestHelper(50, 50);
|
||||
drawingTestHelper.drawView(button);
|
||||
|
||||
int googleBluePixelCount = 0;
|
||||
for (int pixel : drawingTestHelper.getPixels()) {
|
||||
if (pixel == GOOGLE_BLUE) {
|
||||
googleBluePixelCount++;
|
||||
int accentPixelCount =
|
||||
countPixelsWithColor(drawingTestHelper.getPixels(), GLIF_ACCENT_COLOR);
|
||||
assertTrue("> 10 pixels should be #4285f4. Found " + accentPixelCount,
|
||||
accentPixelCount > 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void drawButton_glifV3_shouldHaveAccentColoredButton()
|
||||
throws InstantiationException, IllegalAccessException {
|
||||
Button button = createButton(R.style.SuwThemeGlifV3_Light);
|
||||
|
||||
DrawingTestHelper drawingTestHelper = new DrawingTestHelper(50, 50);
|
||||
drawingTestHelper.drawView(button);
|
||||
|
||||
int accentPixelCount =
|
||||
countPixelsWithColor(drawingTestHelper.getPixels(), GLIF_V3_ACCENT_COLOR);
|
||||
assertTrue("> 10 pixels should be #1a73e8. Found " + accentPixelCount,
|
||||
accentPixelCount > 10);
|
||||
}
|
||||
|
||||
private Button createButton(@StyleRes int theme)
|
||||
throws InstantiationException, IllegalAccessException {
|
||||
final ViewGroup parent = new LinearLayout(DrawingTestHelper.createCanvasActivity(theme));
|
||||
TestButtonItem item = new TestButtonItem();
|
||||
item.setTheme(R.style.SuwButtonItem_Colored);
|
||||
item.setText("foobar");
|
||||
|
||||
return item.createButton(parent);
|
||||
}
|
||||
|
||||
private int countPixelsWithColor(int[] pixels, int color) {
|
||||
int count = 0;
|
||||
for (int pixel : pixels) {
|
||||
if (pixel == color) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
assertTrue("> 10 pixels should be Google blue. Found " + googleBluePixelCount,
|
||||
googleBluePixelCount > 10);
|
||||
return count;
|
||||
}
|
||||
|
||||
private static class TestButtonItem extends ButtonItem {
|
||||
|
|
|
@ -14,29 +14,35 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.setupwizardlib.test;
|
||||
package com.android.setupwizardlib.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Matchers.same;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.support.v4.text.BidiFormatter;
|
||||
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
|
||||
import android.support.v4.widget.ExploreByTouchHelper;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.setupwizardlib.span.LinkSpan;
|
||||
import com.android.setupwizardlib.util.LinkAccessibilityHelper;
|
||||
import com.android.setupwizardlib.util.LinkAccessibilityHelper.PreOLinkAccessibilityHelper;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -52,13 +58,12 @@ public class LinkAccessibilityHelperTest {
|
|||
private static final LinkSpan LINK_SPAN = new LinkSpan("foobar");
|
||||
|
||||
private TextView mTextView;
|
||||
private TestLinkAccessibilityHelper mHelper;
|
||||
private TestPreOLinkAccessibilityHelper mHelper;
|
||||
|
||||
private DisplayMetrics mDisplayMetrics;
|
||||
|
||||
@Test
|
||||
public void testGetVirtualViewAt() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
|
||||
initTextView();
|
||||
final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(15), dp2Px(10));
|
||||
assertEquals("Virtual view ID should be 1", 1, virtualViewId);
|
||||
|
@ -66,7 +71,6 @@ public class LinkAccessibilityHelperTest {
|
|||
|
||||
@Test
|
||||
public void testGetVirtualViewAtHost() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
|
||||
initTextView();
|
||||
final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(100), dp2Px(100));
|
||||
assertEquals("Virtual view ID should be INVALID_ID",
|
||||
|
@ -75,7 +79,6 @@ public class LinkAccessibilityHelperTest {
|
|||
|
||||
@Test
|
||||
public void testGetVisibleVirtualViews() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
|
||||
initTextView();
|
||||
List<Integer> virtualViewIds = new ArrayList<>();
|
||||
mHelper.getVisibleVirtualViews(virtualViewIds);
|
||||
|
@ -86,7 +89,6 @@ public class LinkAccessibilityHelperTest {
|
|||
|
||||
@Test
|
||||
public void testOnPopulateEventForVirtualView() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
|
||||
initTextView();
|
||||
AccessibilityEvent event = AccessibilityEvent.obtain();
|
||||
mHelper.onPopulateEventForVirtualView(1, event);
|
||||
|
@ -100,7 +102,6 @@ public class LinkAccessibilityHelperTest {
|
|||
|
||||
@Test
|
||||
public void testOnPopulateEventForVirtualViewHost() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
|
||||
initTextView();
|
||||
AccessibilityEvent event = AccessibilityEvent.obtain();
|
||||
mHelper.onPopulateEventForVirtualView(ExploreByTouchHelper.INVALID_ID, event);
|
||||
|
@ -113,7 +114,6 @@ public class LinkAccessibilityHelperTest {
|
|||
|
||||
@Test
|
||||
public void testOnPopulateNodeForVirtualView() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
|
||||
initTextView();
|
||||
AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
|
||||
mHelper.onPopulateNodeForVirtualView(1, info);
|
||||
|
@ -132,7 +132,6 @@ public class LinkAccessibilityHelperTest {
|
|||
|
||||
@Test
|
||||
public void testNullLayout() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
|
||||
initTextView();
|
||||
// Setting the padding will cause the layout to be null-ed out.
|
||||
mTextView.setPadding(1, 1, 1, 1);
|
||||
|
@ -150,7 +149,6 @@ public class LinkAccessibilityHelperTest {
|
|||
|
||||
@Test
|
||||
public void testRtlLayout() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder("מכונה בתרגום");
|
||||
ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */);
|
||||
initTextView(ssb);
|
||||
|
@ -170,7 +168,6 @@ public class LinkAccessibilityHelperTest {
|
|||
|
||||
@Test
|
||||
public void testMultilineLink() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder(
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
|
||||
+ "Praesent accumsan efficitur eros eu porttitor.");
|
||||
|
@ -192,7 +189,6 @@ public class LinkAccessibilityHelperTest {
|
|||
|
||||
@Test
|
||||
public void testRtlMultilineLink() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
|
||||
String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
|
||||
+ "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
|
||||
+ "דפים המחשב מיזמים ב.";
|
||||
|
@ -216,7 +212,6 @@ public class LinkAccessibilityHelperTest {
|
|||
|
||||
@Test
|
||||
public void testBidiMultilineLink() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
|
||||
String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
|
||||
+ "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
|
||||
+ "דפים המחשב מיזמים ב.";
|
||||
|
@ -243,6 +238,70 @@ public class LinkAccessibilityHelperTest {
|
|||
info.recycle();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodDelegation() {
|
||||
initTextView();
|
||||
ExploreByTouchHelper delegate = mock(TestPreOLinkAccessibilityHelper.class);
|
||||
LinkAccessibilityHelper helper = new LinkAccessibilityHelper(delegate);
|
||||
|
||||
AccessibilityEvent accessibilityEvent =
|
||||
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_CLICKED);
|
||||
|
||||
helper.sendAccessibilityEvent(mTextView, AccessibilityEvent.TYPE_VIEW_CLICKED);
|
||||
verify(delegate).sendAccessibilityEvent(
|
||||
same(mTextView),
|
||||
eq(AccessibilityEvent.TYPE_VIEW_CLICKED));
|
||||
|
||||
helper.sendAccessibilityEventUnchecked(mTextView, accessibilityEvent);
|
||||
verify(delegate).sendAccessibilityEventUnchecked(same(mTextView), same(accessibilityEvent));
|
||||
|
||||
helper.performAccessibilityAction(
|
||||
mTextView,
|
||||
AccessibilityActionCompat.ACTION_CLICK.getId(),
|
||||
Bundle.EMPTY);
|
||||
verify(delegate).performAccessibilityAction(
|
||||
same(mTextView),
|
||||
eq(AccessibilityActionCompat.ACTION_CLICK.getId()),
|
||||
eq(Bundle.EMPTY));
|
||||
|
||||
helper.dispatchPopulateAccessibilityEvent(
|
||||
mTextView,
|
||||
accessibilityEvent);
|
||||
verify(delegate).dispatchPopulateAccessibilityEvent(
|
||||
same(mTextView),
|
||||
same(accessibilityEvent));
|
||||
|
||||
MotionEvent motionEvent = MotionEvent.obtain(0, 0, 0, 0, 0, 0);
|
||||
helper.dispatchHoverEvent(motionEvent);
|
||||
verify(delegate).dispatchHoverEvent(eq(motionEvent));
|
||||
|
||||
helper.getAccessibilityNodeProvider(mTextView);
|
||||
verify(delegate).getAccessibilityNodeProvider(same(mTextView));
|
||||
|
||||
helper.onInitializeAccessibilityEvent(mTextView, accessibilityEvent);
|
||||
verify(delegate).onInitializeAccessibilityEvent(
|
||||
same(mTextView),
|
||||
eq(accessibilityEvent));
|
||||
|
||||
AccessibilityNodeInfoCompat accessibilityNodeInfo = AccessibilityNodeInfoCompat.obtain();
|
||||
helper.onInitializeAccessibilityNodeInfo(mTextView, accessibilityNodeInfo);
|
||||
verify(delegate).onInitializeAccessibilityNodeInfo(
|
||||
same(mTextView),
|
||||
same(accessibilityNodeInfo));
|
||||
|
||||
helper.onPopulateAccessibilityEvent(mTextView, accessibilityEvent);
|
||||
verify(delegate).onPopulateAccessibilityEvent(
|
||||
same(mTextView),
|
||||
same(accessibilityEvent));
|
||||
|
||||
FrameLayout parent = new FrameLayout(InstrumentationRegistry.getTargetContext());
|
||||
helper.onRequestSendAccessibilityEvent(parent, mTextView, accessibilityEvent);
|
||||
verify(delegate).onRequestSendAccessibilityEvent(
|
||||
same(parent),
|
||||
same(mTextView),
|
||||
same(accessibilityEvent));
|
||||
}
|
||||
|
||||
private void initTextView() {
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
|
||||
ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */);
|
||||
|
@ -254,7 +313,7 @@ public class LinkAccessibilityHelperTest {
|
|||
mTextView.setSingleLine(false);
|
||||
mTextView.setText(text);
|
||||
mTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
|
||||
mHelper = new TestLinkAccessibilityHelper(mTextView);
|
||||
mHelper = new TestPreOLinkAccessibilityHelper(mTextView);
|
||||
|
||||
int measureExactly500dp = View.MeasureSpec.makeMeasureSpec(dp2Px(500),
|
||||
View.MeasureSpec.EXACTLY);
|
||||
|
@ -270,9 +329,9 @@ public class LinkAccessibilityHelperTest {
|
|||
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mDisplayMetrics);
|
||||
}
|
||||
|
||||
private static class TestLinkAccessibilityHelper extends LinkAccessibilityHelper {
|
||||
public static class TestPreOLinkAccessibilityHelper extends PreOLinkAccessibilityHelper {
|
||||
|
||||
TestLinkAccessibilityHelper(TextView view) {
|
||||
TestPreOLinkAccessibilityHelper(TextView view) {
|
||||
super(view);
|
||||
}
|
||||
|
|
@ -16,35 +16,29 @@
|
|||
|
||||
package com.android.setupwizardlib.items;
|
||||
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.robolectric.RuntimeEnvironment.application;
|
||||
|
||||
import android.support.v7.widget.SwitchCompat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.R;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
import com.android.setupwizardlib.view.CheckableLinearLayout;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
public class ExpandableSwitchItemTest {
|
||||
|
||||
private TextView mSummaryView;
|
||||
|
@ -71,6 +65,14 @@ public class ExpandableSwitchItemTest {
|
|||
assertEquals("Should be collapsed initially", "TestSummary", mItem.getSummary());
|
||||
assertEquals("Summary view should display collapsed summary",
|
||||
"TestSummary", mSummaryView.getText());
|
||||
|
||||
assertFalse("Expandable switch item itself should not be focusable", view.isFocusable());
|
||||
|
||||
View switchContent = view.findViewById(R.id.suw_items_expandable_switch_content);
|
||||
assertThat(switchContent).isInstanceOf(CheckableLinearLayout.class);
|
||||
assertThat(switchContent.isFocusable())
|
||||
.named("expandable content focusable")
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -132,57 +134,25 @@ public class ExpandableSwitchItemTest {
|
|||
mItem.onBindView(view);
|
||||
|
||||
final View titleView = view.findViewById(R.id.suw_items_title);
|
||||
assertThat("state_checked should not be set initially",
|
||||
toArrayList(titleView.getDrawableState()),
|
||||
not(hasItem(android.R.attr.state_checked)));
|
||||
assertThat(titleView.getDrawableState()).asList().named("Drawable state")
|
||||
.doesNotContain(android.R.attr.state_checked);
|
||||
|
||||
mItem.setExpanded(true);
|
||||
mItem.onBindView(view);
|
||||
assertThat("state_checked should not be set initially",
|
||||
toArrayList(titleView.getDrawableState()),
|
||||
hasItem(android.R.attr.state_checked));
|
||||
assertThat(titleView.getDrawableState()).asList().named("Drawable state")
|
||||
.contains(android.R.attr.state_checked);
|
||||
|
||||
mItem.setExpanded(false);
|
||||
mItem.onBindView(view);
|
||||
assertThat("state_checked should not be set initially",
|
||||
toArrayList(titleView.getDrawableState()),
|
||||
not(hasItem(android.R.attr.state_checked)));
|
||||
}
|
||||
|
||||
private ArrayList<Integer> toArrayList(int[] array) {
|
||||
ArrayList<Integer> arrayList = new ArrayList<>(array.length);
|
||||
for (int i : array) {
|
||||
arrayList.add(i);
|
||||
}
|
||||
return arrayList;
|
||||
assertThat(titleView.getDrawableState()).asList().named("Drawable state")
|
||||
.doesNotContain(android.R.attr.state_checked);
|
||||
}
|
||||
|
||||
private ViewGroup createLayout() {
|
||||
ViewGroup root = new FrameLayout(application);
|
||||
|
||||
ViewGroup content = new FrameLayout(application);
|
||||
content.setId(R.id.suw_items_expandable_switch_content);
|
||||
root.addView(content);
|
||||
|
||||
TextView titleView = new TextView(application);
|
||||
titleView.setId(R.id.suw_items_title);
|
||||
content.addView(titleView);
|
||||
|
||||
mSummaryView = new TextView(application);
|
||||
mSummaryView.setId(R.id.suw_items_summary);
|
||||
content.addView(mSummaryView);
|
||||
|
||||
FrameLayout iconContainer = new FrameLayout(application);
|
||||
iconContainer.setId(R.id.suw_items_icon_container);
|
||||
content.addView(iconContainer);
|
||||
|
||||
ImageView iconView = new ImageView(application);
|
||||
iconView.setId(R.id.suw_items_icon);
|
||||
iconContainer.addView(iconView);
|
||||
|
||||
SwitchCompat switchView = new SwitchCompat(application);
|
||||
switchView.setId(R.id.suw_items_switch);
|
||||
root.addView(switchView);
|
||||
ViewGroup root =
|
||||
(ViewGroup) LayoutInflater.from(application)
|
||||
.inflate(R.layout.suw_items_expandable_switch, null);
|
||||
mSummaryView = root.findViewById(R.id.suw_items_summary);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
|
|
@ -16,12 +16,15 @@
|
|||
|
||||
package com.android.setupwizardlib.items;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.robolectric.RuntimeEnvironment.application;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.support.v7.widget.SwitchCompat;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -31,20 +34,29 @@ import android.widget.FrameLayout;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.R;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
public class SwitchItemTest {
|
||||
|
||||
private SwitchCompat mSwitch;
|
||||
|
||||
@Test
|
||||
public void testLayout() {
|
||||
Assume.assumeTrue(VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP);
|
||||
SwitchItem item = new SwitchItem();
|
||||
LayoutInflater inflater = LayoutInflater.from(application);
|
||||
ViewGroup layout = (ViewGroup) inflater.inflate(item.getDefaultLayoutResource(), null);
|
||||
assertThat(layout.getClipToPadding()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChecked() {
|
||||
SwitchItem item = new SwitchItem();
|
||||
|
|
|
@ -25,7 +25,6 @@ import android.util.DisplayMetrics;
|
|||
import android.util.TypedValue;
|
||||
import android.view.ContextThemeWrapper;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.R;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
||||
|
@ -35,7 +34,7 @@ import org.junit.runner.RunWith;
|
|||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(constants = BuildConfig.class, sdk = Config.ALL_SDKS)
|
||||
@Config(sdk = Config.ALL_SDKS)
|
||||
public class DimensionConsistencyTest {
|
||||
|
||||
// Visual height of the framework switch widget
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
<!-- SUW lib prefixes styleable resources -->
|
||||
<issue id="CustomViewStyleable" severity="ignore" />
|
||||
<issue id="ExtraTranslation" severity="ignore" />
|
||||
<issue id="GradleDependency" severity="ignore" />
|
||||
<issue id="MissingTranslation" severity="ignore" />
|
||||
<!-- Stop lint from complaining about SDK version checks in the "platform" variant -->
|
||||
<issue id="ObsoleteSdkInt" severity="ignore" />
|
||||
<issue id="RtlEnabled" severity="ignore" />
|
||||
</lint>
|
||||
|
|
21
library/main/res/drawable-v21/suw_edit_text_bg_shape.xml
Normal file
21
library/main/res/drawable-v21/suw_edit_text_bg_shape.xml
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:topLeftRadius="4dp" android:topRightRadius="4dp"/>
|
||||
<solid android:color="?attr/suwEditTextBackgroundColor"/>
|
||||
</shape>
|
52
library/main/res/drawable-v21/suw_edittext_bg.xml
Normal file
52
library/main/res/drawable-v21/suw_edittext_bg.xml
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false">
|
||||
<layer-list>
|
||||
<item android:drawable="@drawable/suw_edit_text_bg_shape" android:bottom="1dp"/>
|
||||
<item android:gravity="bottom">
|
||||
<shape>
|
||||
<size android:height="1dp"/>
|
||||
<solid android:color="?android:attr/textColorSecondary"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
</item>
|
||||
<item android:state_focused="false" android:state_pressed="false">
|
||||
<layer-list>
|
||||
<item android:drawable="@drawable/suw_edit_text_bg_shape" android:bottom="1dp" />
|
||||
<item android:gravity="bottom">
|
||||
<shape>
|
||||
<size android:height="1dp"/>
|
||||
<solid android:color="?android:attr/textColorSecondary"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
</item>
|
||||
<item>
|
||||
<layer-list>
|
||||
<item android:drawable="@drawable/suw_edit_text_bg_shape" android:bottom="2dp" />
|
||||
<item android:gravity="bottom">
|
||||
<shape>
|
||||
<size android:height="2dp"/>
|
||||
<solid android:color="?android:attr/colorAccent"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
</item>
|
||||
</selector>
|
194
library/main/res/drawable-v21/suw_fourcolor_progress_bar.xml
Normal file
194
library/main/res/drawable-v21/suw_fourcolor_progress_bar.xml
Normal file
|
@ -0,0 +1,194 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
|
||||
<!-- Asset for 4 color indeterminate progress bar, which is a ring with 4 shades of blue -->
|
||||
<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="MissingPrefix">
|
||||
<!-- Ignore MissingPrefix: aapt:attr tags take the name attribute without "android:" prefix -->
|
||||
<!-- TODO(yukl): Update to a newer version of lint which properly handles this case -->
|
||||
<aapt:attr name="android:drawable">
|
||||
<vector android:width="823dp" android:height="823dp" android:viewportHeight="823"
|
||||
android:viewportWidth="823">
|
||||
<group android:name="blue1" android:translateX="411.5" android:translateY="411.5">
|
||||
<path android:name="blue1_path"
|
||||
android:pathData="M0 -395 C218,-395 395,-218 395,0 C395,218 218,395 0,395 C-218,395 -395,218 -395,0 C-395,-218 -218,-395 0,-395c "
|
||||
android:strokeAlpha="1" android:strokeColor="#4688f1"
|
||||
android:strokeLineCap="round" android:strokeLineJoin="round"
|
||||
android:strokeWidth="27" />
|
||||
</group>
|
||||
<group android:name="blue2" android:translateX="411.5" android:translateY="411.5">
|
||||
<path android:name="blue2_path"
|
||||
android:pathData=" M0 -395 C218,-395 395,-218 395,0 C395,218 218,395 0,395 C-218,395 -395,218 -395,0 C-395,-218 -218,-395 0,-395c "
|
||||
android:strokeAlpha="1" android:strokeColor="#7dacf4"
|
||||
android:strokeLineCap="round" android:strokeLineJoin="round"
|
||||
android:strokeWidth="28" />
|
||||
</group>
|
||||
<group android:name="blue3" android:translateX="411.5" android:translateY="411.5">
|
||||
<path android:name="blue3_path"
|
||||
android:pathData=" M0 -395 C218,-395 395,-218 395,0 C395,218 218,395 0,395 C-218,395 -395,218 -395,0 C-395,-218 -218,-395 0,-395c "
|
||||
android:strokeAlpha="1" android:strokeColor="#c7dbfb"
|
||||
android:strokeLineCap="round" android:strokeLineJoin="round"
|
||||
android:strokeWidth="29" />
|
||||
</group>
|
||||
<group android:name="blue4" android:translateX="411.5" android:translateY="411.5">
|
||||
<path android:name="blue4_path"
|
||||
android:pathData=" M0 -395 C218,-395 395,-218 395,0 C395,218 218,395 0,395 C-218,395 -395,218 -395,0 C-395,-218 -218,-395 0,-395c "
|
||||
android:strokeAlpha="1" android:strokeColor="#e8f0fd"
|
||||
android:strokeLineCap="round" android:strokeLineJoin="round"
|
||||
android:strokeWidth="30" />
|
||||
</group>
|
||||
</vector>
|
||||
</aapt:attr>
|
||||
<target android:name="blue1_path">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator android:duration="1983" android:propertyName="trimPathStart"
|
||||
android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
|
||||
android:valueType="floatType">
|
||||
<aapt:attr name="android:interpolator">
|
||||
<pathInterpolator android:pathData="M0,0 l.21,0 c.571,0 .194,.755 .79,1" />
|
||||
</aapt:attr>
|
||||
</objectAnimator>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="blue1_path">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator android:duration="1983" android:propertyName="trimPathEnd"
|
||||
android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
|
||||
android:valueType="floatType">
|
||||
<aapt:attr name="android:interpolator">
|
||||
<pathInterpolator android:pathData="M0,0 c0.606,0.315 0.2,1 1.0,1.0" />
|
||||
</aapt:attr>
|
||||
</objectAnimator>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="blue1">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator android:duration="1983" android:propertyName="rotation"
|
||||
android:repeatCount="infinite" android:valueFrom="0" android:valueTo="355"
|
||||
android:valueType="floatType">
|
||||
<aapt:attr name="android:interpolator">
|
||||
<pathInterpolator android:pathData="M0,0 c0.829,0.228 0.2,0.915 1.0,1.0" />
|
||||
</aapt:attr>
|
||||
</objectAnimator>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="blue2_path">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator android:duration="1983" android:propertyName="trimPathStart"
|
||||
android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
|
||||
android:valueType="floatType">
|
||||
<aapt:attr name="android:interpolator">
|
||||
<pathInterpolator android:pathData="M0,0 l.21,0 c.571,0 .145,.831 .79,1" />
|
||||
</aapt:attr>
|
||||
</objectAnimator>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="blue2_path">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator android:duration="1983" android:propertyName="trimPathEnd"
|
||||
android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
|
||||
android:valueType="floatType">
|
||||
<aapt:attr name="android:interpolator">
|
||||
<pathInterpolator android:pathData="M0,0 c0.606,0.315 0.2,1 1.0,1.0" />
|
||||
</aapt:attr>
|
||||
</objectAnimator>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="blue2">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator android:duration="1983" android:propertyName="rotation"
|
||||
android:repeatCount="infinite" android:valueFrom="0" android:valueTo="355"
|
||||
android:valueType="floatType">
|
||||
<aapt:attr name="android:interpolator">
|
||||
<pathInterpolator android:pathData="M0,0 c0.792,0.233 0.2,0.915 1.0,1.0" />
|
||||
</aapt:attr>
|
||||
</objectAnimator>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="blue3_path">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator android:duration="1983" android:propertyName="trimPathStart"
|
||||
android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
|
||||
android:valueType="floatType">
|
||||
<aapt:attr name="android:interpolator">
|
||||
<pathInterpolator android:pathData="M0,0 l.21,0 c.6138,0 .007,.883 .79,1" />
|
||||
</aapt:attr>
|
||||
</objectAnimator>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="blue3_path">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator android:duration="1983" android:propertyName="trimPathEnd"
|
||||
android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
|
||||
android:valueType="floatType">
|
||||
<aapt:attr name="android:interpolator">
|
||||
<pathInterpolator android:pathData="M0,0 c0.606,0.315 0.2,1 1.0,1.0" />
|
||||
</aapt:attr>
|
||||
</objectAnimator>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="blue3">
|
||||
<aapt:attr name="android:animation">
|
||||
<set android:ordering="together">
|
||||
<objectAnimator android:duration="1983" android:propertyName="rotation"
|
||||
android:repeatCount="infinite" android:valueFrom="0" android:valueTo="355"
|
||||
android:valueType="floatType">
|
||||
<aapt:attr name="android:interpolator">
|
||||
<pathInterpolator
|
||||
android:pathData="M0,0 c0.762,0.225 0.2,0.915 1.0,1.0" />
|
||||
</aapt:attr>
|
||||
</objectAnimator>
|
||||
</set>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="blue4_path">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator android:duration="1983" android:propertyName="trimPathStart"
|
||||
android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
|
||||
android:valueType="floatType">
|
||||
<aapt:attr name="android:interpolator">
|
||||
<pathInterpolator android:pathData="M0,0 l.21,0 c.572,0 0,1 .79,1" />
|
||||
</aapt:attr>
|
||||
</objectAnimator>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="blue4_path">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator android:duration="1983" android:propertyName="trimPathEnd"
|
||||
android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
|
||||
android:valueType="floatType">
|
||||
<aapt:attr name="android:interpolator">
|
||||
<pathInterpolator android:pathData="M0,0 c0.606,0.315 0.2,1 1.0,1.0" />
|
||||
</aapt:attr>
|
||||
</objectAnimator>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="blue4">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator android:duration="1983" android:propertyName="rotation"
|
||||
android:repeatCount="infinite" android:valueFrom="0" android:valueTo="355"
|
||||
android:valueType="floatType">
|
||||
<aapt:attr name="android:interpolator">
|
||||
<pathInterpolator android:pathData="M0,0 c0.606,0.172 0.2,0.915 1.0,1.0" />
|
||||
</aapt:attr>
|
||||
</objectAnimator>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
</animated-vector>
|
|
@ -21,6 +21,11 @@
|
|||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/suw_layout_sticky_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/suw_layout_content"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -23,10 +23,11 @@
|
|||
|
||||
<ImageView
|
||||
android:id="@+id/suw_layout_icon"
|
||||
style="@style/SuwGlifIcon"
|
||||
style="?attr/suwGlifIconStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null" />
|
||||
android:contentDescription="@null"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/suw_layout_title"
|
||||
|
|
|
@ -22,6 +22,11 @@
|
|||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/suw_layout_sticky_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<!-- Ignore UnusedAttribute: scrollIndicators is new in M. Default to no indicators in older
|
||||
versions. -->
|
||||
<com.android.setupwizardlib.view.StickyHeaderListView
|
||||
|
|
40
library/main/res/layout/suw_glif_loading_screen.xml
Normal file
40
library/main/res/layout/suw_glif_loading_screen.xml
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<com.android.setupwizardlib.GlifLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/setup_wizard_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:ignore="UnusedResources">
|
||||
<!-- Ignore UnusedResources: can be used by clients -->
|
||||
|
||||
<com.android.setupwizardlib.view.FillContentLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/suw_large_progress_bar"
|
||||
style="@style/SuwFourColorIndeterminateProgressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</com.android.setupwizardlib.view.FillContentLayout>
|
||||
|
||||
</com.android.setupwizardlib.GlifLayout>
|
|
@ -22,6 +22,11 @@
|
|||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/suw_layout_sticky_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<!-- Ignore UnusedAttribute: scrollIndicators is new in M. Default to no indicators in older
|
||||
versions. -->
|
||||
<com.android.setupwizardlib.view.BottomScrollView
|
||||
|
|
23
library/main/res/values-en-rCA/strings.xml
Normal file
23
library/main/res/values-en-rCA/strings.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="suw_next_button_label" msgid="7269625133873553978">"Next"</string>
|
||||
<string name="suw_back_button_label" msgid="1460929053642711025">"Back"</string>
|
||||
<string name="suw_more_button_label" msgid="7769076059705546563">"More"</string>
|
||||
</resources>
|
23
library/main/res/values-en-rXC/strings.xml
Normal file
23
library/main/res/values-en-rXC/strings.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="suw_next_button_label" msgid="7269625133873553978">"Next"</string>
|
||||
<string name="suw_back_button_label" msgid="1460929053642711025">"Back"</string>
|
||||
<string name="suw_more_button_label" msgid="7769076059705546563">"More"</string>
|
||||
</resources>
|
22
library/main/res/values-v11/styles.xml
Normal file
22
library/main/res/values-v11/styles.xml
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<style name="SuwBase.ProgressBarLarge" parent="@android:style/Widget.Holo.ProgressBar.Large" />
|
||||
|
||||
</resources>
|
|
@ -42,6 +42,16 @@
|
|||
<item name="android:fontFamily">sans-serif-medium</item>
|
||||
</style>
|
||||
|
||||
<style name="SuwBase.ProgressBarLarge" parent="@android:style/Widget.Material.ProgressBar.Large" />
|
||||
|
||||
<style name="SuwFourColorIndeterminateProgressBar" parent="SuwBase.ProgressBarLarge">
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:indeterminate">true</item>
|
||||
<item name="android:indeterminateDrawable">@drawable/suw_fourcolor_progress_bar</item>
|
||||
<item name="android:indeterminateTint">@null</item>
|
||||
<item name="android:indeterminateTintMode">@null</item>
|
||||
</style>
|
||||
|
||||
<!-- Items styles -->
|
||||
|
||||
<style name="SuwItemContainer">
|
||||
|
@ -83,4 +93,11 @@
|
|||
<item name="suwNavBarButtonBackground">@drawable/suw_navbar_btn_bg</item>
|
||||
</style>
|
||||
|
||||
<style name="SuwEditText" parent="@android:style/Widget.Material.EditText">
|
||||
<item name="android:background">@drawable/suw_edittext_bg</item>
|
||||
<item name="android:minHeight">@dimen/suw_edit_text_min_height</item>
|
||||
<item name="android:paddingLeft">@dimen/suw_edit_text_padding_horizontal</item>
|
||||
<item name="android:paddingRight">@dimen/suw_edit_text_padding_horizontal</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
|
23
library/main/res/values-v22/styles.xml
Normal file
23
library/main/res/values-v22/styles.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<style name="SuwAlertDialogTheme" parent="android:Theme.DeviceDefault.Dialog.Alert" />
|
||||
|
||||
<style name="SuwAlertDialogTheme.Light" parent="android:Theme.DeviceDefault.Light.Dialog.Alert" />
|
||||
</resources>
|
|
@ -20,6 +20,7 @@
|
|||
<!-- Theme attributes -->
|
||||
<attr name="suwLayoutTheme" format="reference" />
|
||||
<attr name="suwMarginSides" format="dimension|reference" />
|
||||
<attr name="suwEditTextBackgroundColor" format="color" />
|
||||
|
||||
<!-- Subset of values in "gravity" in frameworks/base/core/res/res/values/attrs.xml. Only
|
||||
horizontal values are listed here as the header does not support vertical gravity. -->
|
||||
|
@ -37,13 +38,17 @@
|
|||
<!-- Push object to the end of its container, not changing its size. -->
|
||||
<flag name="end" value="0x00800005" />
|
||||
</attr>
|
||||
<attr name="suwGlifIconStyle" format="reference" />
|
||||
|
||||
<attr name="suwButtonAllCaps" format="boolean" />
|
||||
<attr name="suwButtonCornerRadius" format="dimension" />
|
||||
<attr name="suwButtonFontFamily" format="string|reference" />
|
||||
<attr name="suwCardBackground" format="color|reference" />
|
||||
<attr name="suwFillContentLayoutStyle" format="reference" />
|
||||
<attr name="suwDividerCondition">
|
||||
<enum name="either" value="0" />
|
||||
<enum name="both" value="1" />
|
||||
</attr>
|
||||
<attr name="suwFillContentLayoutStyle" format="reference" />
|
||||
<attr name="suwListItemIconColor" format="color" />
|
||||
<attr name="suwNavBarBackgroundColor" format="color" />
|
||||
<attr name="suwNavBarButtonBackground" format="color|reference" />
|
||||
|
@ -102,6 +107,8 @@
|
|||
<attr name="suwBackgroundBaseColor" format="color" />
|
||||
<attr name="suwColorPrimary" />
|
||||
<attr name="suwFooter" format="reference" />
|
||||
<attr name="suwLayoutFullscreen" format="boolean" />
|
||||
<attr name="suwStickyHeader" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SuwStatusBarBackgroundLayout">
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
<color name="suw_color_accent_dark">#ff448aff</color>
|
||||
<color name="suw_color_accent_light">#ff3367d6</color>
|
||||
<color name="suw_color_background_dark">#ff303030</color>
|
||||
<color name="suw_color_background_light">#fffafafa</color>
|
||||
<color name="suw_link_color_dark">#ff448aff</color>
|
||||
<color name="suw_link_color_light">#ff3367d6</color>
|
||||
<color name="suw_list_item_icon_color_dark">#b3ffffff</color>
|
||||
|
@ -40,7 +42,11 @@
|
|||
<!-- GLIF colors -->
|
||||
<color name="suw_color_accent_glif_dark">#ff4285f4</color>
|
||||
<color name="suw_color_accent_glif_light">#ff4285f4</color>
|
||||
<color name="suw_color_accent_glif_v3">#ff1a73e8</color>
|
||||
<color name="suw_glif_background_color_dark">#ff000000</color>
|
||||
<color name="suw_glif_background_color_light">#ffffffff</color>
|
||||
<color name="suw_glif_edit_text_bg_light_color">#fff1f3f4</color>
|
||||
<color name="suw_glif_v3_nav_bar_color_light">#ffffffff</color>
|
||||
<color name="suw_glif_v3_nav_bar_divider_color_light">#1f000000</color>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -27,4 +27,8 @@
|
|||
ButtonBarLayout -->
|
||||
<item name="suw_original_weight" type="id" />
|
||||
|
||||
<!-- Secondary font for use with headings, title, and other non-body text -->
|
||||
<string name="suwFontSecondary" translatable="false">google-sans</string>
|
||||
<string name="suwFontSecondaryMedium" translatable="false">google-sans-medium</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -20,16 +20,21 @@
|
|||
<!-- General -->
|
||||
<dimen name="suw_layout_margin_sides">40dp</dimen>
|
||||
|
||||
<dimen name="suw_glif_button_corner_radius">2dp</dimen>
|
||||
<!-- Calculated by (suw_glif_margin_sides - 4dp internal padding of button) -->
|
||||
<dimen name="suw_glif_button_margin_end">20dp</dimen>
|
||||
<!-- Calculated by (suw_glif_margin_sides - suw_glif_button_padding) -->
|
||||
<dimen name="suw_glif_button_margin_start">8dp</dimen>
|
||||
<dimen name="suw_glif_button_padding">16dp</dimen>
|
||||
<!-- Negative of suw_glif_button_padding -->
|
||||
<dimen name="suw_glif_negative_button_padding">-16dp</dimen>
|
||||
<dimen name="suw_glif_footer_padding_vertical">8dp</dimen>
|
||||
<dimen name="suw_glif_footer_min_height">80dp</dimen>
|
||||
<dimen name="suw_glif_footer_min_height">72dp</dimen>
|
||||
<dimen name="suw_glif_margin_sides">24dp</dimen>
|
||||
<dimen name="suw_glif_margin_top">48dp</dimen>
|
||||
|
||||
<dimen name="suw_glif_v3_button_corner_radius">4dp</dimen>
|
||||
|
||||
<!-- Content styles -->
|
||||
<dimen name="suw_check_box_line_spacing_extra">4sp</dimen>
|
||||
<dimen name="suw_check_box_margin_bottom">12dp</dimen>
|
||||
|
@ -135,4 +140,8 @@
|
|||
<dimen name="suw_progress_bar_margin_vertical">-7dp</dimen>
|
||||
<dimen name="suw_glif_progress_bar_margin_vertical">7dp</dimen>
|
||||
|
||||
<!-- Edit Text dimensions -->
|
||||
<dimen name="suw_edit_text_min_height">56dp</dimen>
|
||||
<dimen name="suw_edit_text_padding_horizontal">12dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -30,11 +30,11 @@
|
|||
<item name="suwDividerInsetStartNoIcon">?attr/suwMarginSides</item>
|
||||
<item name="suwGlifHeaderGravity">center_horizontal</item>
|
||||
<item name="suwScrollIndicators">top|bottom</item>
|
||||
<item name="suwEditTextBackgroundColor">@color/suw_glif_edit_text_bg_light_color</item> <!-- TODO: Change color -->
|
||||
<item name="android:editTextStyle">@style/SuwEditText</item>
|
||||
<item name="android:alertDialogTheme" tools:targetApi="honeycomb">@style/SuwAlertDialogTheme</item>
|
||||
</style>
|
||||
|
||||
<!-- Deprecated. Use SuwThemeGlifV2 instead -->
|
||||
<style name="SuwThemeGlifPixel" parent="SuwThemeGlifV2" />
|
||||
|
||||
<style name="SuwThemeGlifV2.Light" parent="SuwThemeGlif.Light">
|
||||
<item name="android:colorBackground">@color/suw_glif_background_color_light</item>
|
||||
<item name="android:windowLightStatusBar" tools:targetApi="m">true</item>
|
||||
|
@ -46,11 +46,11 @@
|
|||
<item name="suwDividerInsetStartNoIcon">?attr/suwMarginSides</item>
|
||||
<item name="suwGlifHeaderGravity">center_horizontal</item>
|
||||
<item name="suwScrollIndicators">top|bottom</item>
|
||||
<item name="suwEditTextBackgroundColor">@color/suw_glif_edit_text_bg_light_color</item>
|
||||
<item name="android:editTextStyle">@style/SuwEditText</item>
|
||||
<item name="android:alertDialogTheme" tools:targetApi="honeycomb">@style/SuwAlertDialogTheme.Light</item>
|
||||
</style>
|
||||
|
||||
<!-- Deprecated. Use SuwThemeGlifV2.Light instead -->
|
||||
<style name="SuwThemeGlifPixel.Light" parent="SuwThemeGlifV2.Light" />
|
||||
|
||||
<style name="Animation.SuwWindowAnimation" parent="@android:style/Animation.Activity">
|
||||
<item name="android:activityOpenEnterAnimation">@anim/suw_slide_next_in</item>
|
||||
<item name="android:activityOpenExitAnimation">@anim/suw_slide_next_out</item>
|
||||
|
@ -86,11 +86,10 @@
|
|||
<item name="android:textAlignment" tools:targetApi="jelly_bean_mr1">gravity</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.SuwDescription.Light" parent="TextAppearance.SuwDescription">
|
||||
<item name="android:fontFamily" tools:ignore="NewApi">sans-serif-light</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.SuwDescription.Secondary" parent="TextAppearance.SuwDescription">
|
||||
<!-- Ignore UnusedResources: Used by clients -->
|
||||
<style name="TextAppearance.SuwDescription.Secondary"
|
||||
parent="TextAppearance.SuwDescription"
|
||||
tools:ignore="UnusedResources">
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
</style>
|
||||
|
||||
|
@ -196,14 +195,19 @@
|
|||
<item name="android:buttonStyle">@style/SuwGlifButton.Tertiary</item>
|
||||
<item name="android:theme">@style/SuwGlifButton.Tertiary</item>
|
||||
|
||||
<item name="android:background">@null</item>
|
||||
<item name="android:fontFamily" tools:targetApi="jelly_bean">sans-serif</item>
|
||||
<item name="android:layout_gravity">?attr/suwGlifHeaderGravity</item>
|
||||
<item name="android:padding">0dp</item>
|
||||
<item name="android:layout_marginLeft">@dimen/suw_glif_negative_button_padding</item>
|
||||
<item name="android:layout_marginRight">@dimen/suw_glif_negative_button_padding</item>
|
||||
<!-- Always lowercase instead of reading attr/suwButtonAllCaps, since this is a tertiary
|
||||
button -->
|
||||
<item name="android:textAllCaps" tools:targetApi="ice_cream_sandwich">false</item>
|
||||
</style>
|
||||
|
||||
<style name="SuwGlifButton.Tertiary" parent="SuwGlifButton.BaseTertiary" />
|
||||
<!-- Ignore UnusedResources: used by clients -->
|
||||
<style name="SuwGlifButton.Tertiary"
|
||||
parent="SuwGlifButton.BaseTertiary"
|
||||
tools:ignore="UnusedResources" />
|
||||
|
||||
<!-- The start and end paddings are asymmetric because start buttons are borderless buttons
|
||||
which aligns the text label. -->
|
||||
|
@ -266,7 +270,7 @@
|
|||
<item name="android:layout_marginLeft">?attr/suwMarginSides</item>
|
||||
<item name="android:layout_marginRight">?attr/suwMarginSides</item>
|
||||
<item name="android:layout_marginTop">@dimen/suw_glif_header_title_margin_top</item>
|
||||
<item name="android:fontFamily" tools:targetApi="jelly_bean">google-sans</item>
|
||||
<item name="android:fontFamily" tools:targetApi="jelly_bean">@string/suwFontSecondary</item>
|
||||
<item name="android:textAlignment" tools:targetApi="jelly_bean_mr1">gravity</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
</style>
|
||||
|
@ -309,4 +313,9 @@
|
|||
<item name="suwNavBarTextColor">?android:attr/textColorPrimary</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="SuwEditText" parent="@android:style/Widget.EditText">
|
||||
<item name="android:minHeight">@dimen/suw_edit_text_min_height</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -77,6 +77,8 @@ public class GlifLayout extends TemplateLayout {
|
|||
@Nullable
|
||||
private ColorStateList mBackgroundBaseColor;
|
||||
|
||||
private boolean mLayoutFullscreen = true;
|
||||
|
||||
public GlifLayout(Context context) {
|
||||
this(context, 0, 0);
|
||||
}
|
||||
|
@ -139,7 +141,18 @@ public class GlifLayout extends TemplateLayout {
|
|||
inflateFooter(footer);
|
||||
}
|
||||
|
||||
final int stickyHeader = a.getResourceId(R.styleable.SuwGlifLayout_suwStickyHeader, 0);
|
||||
if (stickyHeader != 0) {
|
||||
inflateStickyHeader(stickyHeader);
|
||||
}
|
||||
|
||||
mLayoutFullscreen = a.getBoolean(R.styleable.SuwGlifLayout_suwLayoutFullscreen, true);
|
||||
|
||||
a.recycle();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && mLayoutFullscreen) {
|
||||
setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -160,17 +173,31 @@ public class GlifLayout extends TemplateLayout {
|
|||
|
||||
/**
|
||||
* Sets the footer of the layout, which is at the bottom of the content area outside the
|
||||
* scrolling container. The footer can only be inflated once per layout.
|
||||
* scrolling container. The footer can only be inflated once per instance of this layout.
|
||||
*
|
||||
* @param footer The layout to be inflated as footer.
|
||||
* @return The root of the inflated footer view.
|
||||
*/
|
||||
public View inflateFooter(@LayoutRes int footer) {
|
||||
ViewStub footerStub = (ViewStub) findManagedViewById(R.id.suw_layout_footer);
|
||||
ViewStub footerStub = findManagedViewById(R.id.suw_layout_footer);
|
||||
footerStub.setLayoutResource(footer);
|
||||
return footerStub.inflate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sticky header (i.e. header that doesn't scroll) of the layout, which is at the top
|
||||
* of the content area outside of the scrolling container. The header can only be inflated once
|
||||
* per instance of this layout.
|
||||
*
|
||||
* @param header The layout to be inflated as the header.
|
||||
* @return The root of the inflated header view.
|
||||
*/
|
||||
public View inflateStickyHeader(@LayoutRes int header) {
|
||||
ViewStub stickyHeaderStub = findManagedViewById(R.id.suw_layout_sticky_header);
|
||||
stickyHeaderStub.setLayoutResource(header);
|
||||
return stickyHeaderStub.inflate();
|
||||
}
|
||||
|
||||
public ScrollView getScrollView() {
|
||||
final View view = findManagedViewById(R.id.suw_scroll_view);
|
||||
return view instanceof ScrollView ? (ScrollView) view : null;
|
||||
|
@ -280,9 +307,6 @@ public class GlifLayout extends TemplateLayout {
|
|||
patternBg.setBackgroundDrawable(background);
|
||||
}
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
||||
setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isProgressBarShown() {
|
||||
|
|
|
@ -23,7 +23,6 @@ import android.graphics.Bitmap;
|
|||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PixelFormat;
|
||||
|
@ -96,7 +95,6 @@ public class GlifPatternDrawable extends Drawable {
|
|||
|
||||
private int mColor;
|
||||
private Paint mTempPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private ColorFilter mColorFilter;
|
||||
|
||||
public GlifPatternDrawable(int color) {
|
||||
setColor(color);
|
||||
|
@ -140,17 +138,10 @@ public class GlifPatternDrawable extends Drawable {
|
|||
canvas.clipRect(bounds);
|
||||
|
||||
scaleCanvasToBounds(canvas, bitmap, bounds);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB
|
||||
&& canvas.isHardwareAccelerated()) {
|
||||
mTempPaint.setColorFilter(mColorFilter);
|
||||
canvas.drawBitmap(bitmap, 0, 0, mTempPaint);
|
||||
} else {
|
||||
// Software renderer doesn't work properly with ColorMatrix filter on ALPHA_8 bitmaps.
|
||||
canvas.drawColor(Color.BLACK);
|
||||
mTempPaint.setColor(Color.WHITE);
|
||||
canvas.drawBitmap(bitmap, 0, 0, mTempPaint);
|
||||
canvas.drawColor(mColor);
|
||||
}
|
||||
canvas.drawColor(Color.BLACK);
|
||||
mTempPaint.setColor(Color.WHITE);
|
||||
canvas.drawBitmap(bitmap, 0, 0, mTempPaint);
|
||||
canvas.drawColor(mColor);
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
|
@ -299,12 +290,6 @@ public class GlifPatternDrawable extends Drawable {
|
|||
final int g = Color.green(color);
|
||||
final int b = Color.blue(color);
|
||||
mColor = Color.argb(COLOR_ALPHA_INT, r, g, b);
|
||||
mColorFilter = new ColorMatrixColorFilter(new float[] {
|
||||
0, 0, 0, 1 - COLOR_ALPHA, r * COLOR_ALPHA,
|
||||
0, 0, 0, 1 - COLOR_ALPHA, g * COLOR_ALPHA,
|
||||
0, 0, 0, 1 - COLOR_ALPHA, b * COLOR_ALPHA,
|
||||
0, 0, 0, 0, 255
|
||||
});
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
|
|
|
@ -103,7 +103,9 @@ public class TemplateLayout extends FrameLayout {
|
|||
* by this view but not currently added to the view hierarchy. e.g. recycler view or list view
|
||||
* headers that are not currently shown.
|
||||
*/
|
||||
public View findManagedViewById(int id) {
|
||||
// Returning generic type is the common pattern used for findViewBy* methods
|
||||
@SuppressWarnings("TypeParameterUnusedInFormals")
|
||||
public <T extends View> T findManagedViewById(int id) {
|
||||
return findViewById(id);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ public final class ConsecutiveTapsGestureDetector {
|
|||
private final View mView;
|
||||
private final OnConsecutiveTapsListener mListener;
|
||||
private final int mConsecutiveTapTouchSlopSquare;
|
||||
private final int mConsecutiveTapTimeout;
|
||||
|
||||
private int mConsecutiveTapsCounter = 0;
|
||||
private MotionEvent mPreviousTapEvent;
|
||||
|
@ -54,6 +55,7 @@ public final class ConsecutiveTapsGestureDetector {
|
|||
mView = view;
|
||||
int doubleTapSlop = ViewConfiguration.get(mView.getContext()).getScaledDoubleTapSlop();
|
||||
mConsecutiveTapTouchSlopSquare = doubleTapSlop * doubleTapSlop;
|
||||
mConsecutiveTapTimeout = ViewConfiguration.getDoubleTapTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,6 +111,8 @@ public final class ConsecutiveTapsGestureDetector {
|
|||
|
||||
double deltaX = mPreviousTapEvent.getX() - currentTapEvent.getX();
|
||||
double deltaY = mPreviousTapEvent.getY() - currentTapEvent.getY();
|
||||
return (deltaX * deltaX + deltaY * deltaY <= mConsecutiveTapTouchSlopSquare);
|
||||
long deltaTime = currentTapEvent.getEventTime() - mPreviousTapEvent.getEventTime();
|
||||
return (deltaX * deltaX + deltaY * deltaY <= mConsecutiveTapTouchSlopSquare)
|
||||
&& deltaTime < mConsecutiveTapTimeout;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ public class ButtonBarItem extends AbstractItem implements ItemInflater.ItemPare
|
|||
return mVisible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewId() {
|
||||
return getId();
|
||||
}
|
||||
|
|
|
@ -21,10 +21,13 @@ import android.content.ContextWrapper;
|
|||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.Selection;
|
||||
import android.text.Spannable;
|
||||
import android.text.TextPaint;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* A clickable span that will listen for click events and send it back to the context. To use this
|
||||
|
@ -86,11 +89,19 @@ public class LinkSpan extends ClickableSpan {
|
|||
public void onClick(View view) {
|
||||
if (dispatchClick(view)) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
// Prevent the touch event from bubbling up to the parent views.
|
||||
view.cancelPendingInputEvents();
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Dropping click event. No listener attached.");
|
||||
}
|
||||
if (view instanceof TextView) {
|
||||
// Remove the highlight effect when the click happens by clearing the selection
|
||||
CharSequence text = ((TextView) view).getText();
|
||||
if (text instanceof Spannable) {
|
||||
Selection.setSelection((Spannable) text, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean dispatchClick(View view) {
|
||||
|
|
|
@ -19,7 +19,9 @@ package com.android.setupwizardlib.template;
|
|||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.setupwizardlib.R;
|
||||
|
@ -44,8 +46,8 @@ public class IconMixin implements Mixin {
|
|||
final TypedArray a =
|
||||
context.obtainStyledAttributes(attrs, R.styleable.SuwIconMixin, defStyleAttr, 0);
|
||||
|
||||
final Drawable icon = a.getDrawable(R.styleable.SuwIconMixin_android_icon);
|
||||
if (icon != null) {
|
||||
final @DrawableRes int icon = a.getResourceId(R.styleable.SuwIconMixin_android_icon, 0);
|
||||
if (icon != 0) {
|
||||
setIcon(icon);
|
||||
}
|
||||
|
||||
|
@ -61,6 +63,22 @@ public class IconMixin implements Mixin {
|
|||
final ImageView iconView = getView();
|
||||
if (iconView != null) {
|
||||
iconView.setImageDrawable(icon);
|
||||
iconView.setVisibility(icon != null ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
|
||||
*
|
||||
* @param icon A drawable icon resource.
|
||||
*/
|
||||
public void setIcon(@DrawableRes int icon) {
|
||||
final ImageView iconView = getView();
|
||||
if (iconView != null) {
|
||||
// Note: setImageResource on the ImageView is overridden in AppCompatImageView for
|
||||
// support lib users, which enables vector drawable compat to work on versions pre-L.
|
||||
iconView.setImageResource(icon);
|
||||
iconView.setVisibility(icon != 0 ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,6 +90,24 @@ public class IconMixin implements Mixin {
|
|||
return iconView != null ? iconView.getDrawable() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content description of the icon view
|
||||
*/
|
||||
public void setContentDescription(CharSequence description) {
|
||||
final ImageView iconView = getView();
|
||||
if (iconView != null) {
|
||||
iconView.setContentDescription(description);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The content description of the icon view
|
||||
*/
|
||||
public CharSequence getContentDescription() {
|
||||
final ImageView iconView = getView();
|
||||
return iconView != null ? iconView.getContentDescription() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The ImageView responsible for displaying the icon.
|
||||
*/
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.graphics.drawable.Drawable;
|
|||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.support.annotation.AnyRes;
|
||||
import android.support.annotation.ColorRes;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
|
@ -75,6 +76,15 @@ public class Partner {
|
|||
return entry.resources.getString(entry.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to get color from partner overlay, or if not available, the color from
|
||||
* the original context.
|
||||
*/
|
||||
public static int getColor(Context context, @ColorRes int id) {
|
||||
final ResourceEntry resourceEntry = getResourceEntry(context, id);
|
||||
return resourceEntry.resources.getColor(resourceEntry.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to get a CharSequence from partner overlay, or if not available, the text
|
||||
* from the original context.
|
||||
|
|
|
@ -24,6 +24,7 @@ import android.content.res.TypedArray;
|
|||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.RequiresPermission;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -183,12 +184,25 @@ public class SystemBarHelper {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the back button on the software navigation bar is visible. This only works if
|
||||
* you have the STATUS_BAR permission. Otherwise framework will filter out this flag and this
|
||||
* method call will not have any effect.
|
||||
*
|
||||
* <p>IMPORTANT: Do not assume that users have no way to go back when the back button is hidden.
|
||||
* Many devices have physical back buttons, and accessibility services like TalkBack may have
|
||||
* gestures mapped to back. Please use onBackPressed, onKeyDown, or other similar ways to
|
||||
* make sure back button events are still handled (or ignored) properly.
|
||||
*/
|
||||
@RequiresPermission("android.permission.STATUS_BAR")
|
||||
public static void setBackButtonVisible(final Window window, final boolean visible) {
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
|
||||
if (visible) {
|
||||
removeVisibilityFlag(window, STATUS_BAR_DISABLE_BACK);
|
||||
removeImmersiveFlagsFromDecorView(window, STATUS_BAR_DISABLE_BACK);
|
||||
} else {
|
||||
addVisibilityFlag(window, STATUS_BAR_DISABLE_BACK);
|
||||
addImmersiveFlagsToDecorView(window, STATUS_BAR_DISABLE_BACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,7 +231,7 @@ public class SystemBarHelper {
|
|||
* {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} only takes effect when it is added to a view
|
||||
* instead of the window.
|
||||
*/
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
@TargetApi(VERSION_CODES.HONEYCOMB)
|
||||
private static void addImmersiveFlagsToDecorView(final Window window, final int vis) {
|
||||
getDecorView(window, new OnDecorViewInstalledListener() {
|
||||
@Override
|
||||
|
@ -227,7 +241,7 @@ public class SystemBarHelper {
|
|||
});
|
||||
}
|
||||
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
@TargetApi(VERSION_CODES.HONEYCOMB)
|
||||
private static void removeImmersiveFlagsFromDecorView(final Window window, final int vis) {
|
||||
getDecorView(window, new OnDecorViewInstalledListener() {
|
||||
@Override
|
||||
|
|
|
@ -27,6 +27,8 @@ import android.support.annotation.VisibleForTesting;
|
|||
|
||||
import com.android.setupwizardlib.R;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class WizardManagerHelper {
|
||||
|
||||
private static final String ACTION_NEXT = "com.android.wizard.NEXT";
|
||||
|
@ -45,6 +47,8 @@ public class WizardManagerHelper {
|
|||
static final String EXTRA_IS_FIRST_RUN = "firstRun";
|
||||
@VisibleForTesting
|
||||
static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup";
|
||||
@VisibleForTesting
|
||||
static final String EXTRA_IS_PRE_DEFERRED_SETUP = "preDeferredSetup";
|
||||
|
||||
public static final String EXTRA_THEME = "theme";
|
||||
public static final String EXTRA_USE_IMMERSIVE_MODE = "useImmersiveMode";
|
||||
|
@ -75,12 +79,6 @@ public class WizardManagerHelper {
|
|||
*/
|
||||
public static final String THEME_GLIF_V2 = "glif_v2";
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #THEME_GLIF_V2} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String THEME_GLIF_PIXEL = THEME_GLIF_V2;
|
||||
|
||||
/**
|
||||
* Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the default theme used in
|
||||
* setup wizard for O DR.
|
||||
|
@ -88,10 +86,16 @@ public class WizardManagerHelper {
|
|||
public static final String THEME_GLIF_V2_LIGHT = "glif_v2_light";
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #THEME_GLIF_V2_LIGHT} instead.
|
||||
* Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the dark variant of the
|
||||
* theme used in setup wizard for P.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String THEME_GLIF_PIXEL_LIGHT = THEME_GLIF_V2_LIGHT;
|
||||
public static final String THEME_GLIF_V3 = "glif_v3";
|
||||
|
||||
/**
|
||||
* Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the default theme used in
|
||||
* setup wizard for P.
|
||||
*/
|
||||
public static final String THEME_GLIF_V3_LIGHT = "glif_v3_light";
|
||||
|
||||
/**
|
||||
* Get an intent that will invoke the next step of setup wizard.
|
||||
|
@ -140,13 +144,14 @@ public class WizardManagerHelper {
|
|||
*/
|
||||
public static void copyWizardManagerExtras(Intent srcIntent, Intent dstIntent) {
|
||||
dstIntent.putExtra(EXTRA_WIZARD_BUNDLE, srcIntent.getBundleExtra(EXTRA_WIZARD_BUNDLE));
|
||||
dstIntent.putExtra(EXTRA_THEME, srcIntent.getStringExtra(EXTRA_THEME));
|
||||
dstIntent.putExtra(EXTRA_IS_FIRST_RUN,
|
||||
srcIntent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false));
|
||||
dstIntent.putExtra(EXTRA_IS_DEFERRED_SETUP,
|
||||
srcIntent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false));
|
||||
dstIntent.putExtra(EXTRA_SCRIPT_URI, srcIntent.getStringExtra(EXTRA_SCRIPT_URI));
|
||||
dstIntent.putExtra(EXTRA_ACTION_ID, srcIntent.getStringExtra(EXTRA_ACTION_ID));
|
||||
for (String key : Arrays.asList(
|
||||
EXTRA_IS_FIRST_RUN, EXTRA_IS_DEFERRED_SETUP, EXTRA_IS_PRE_DEFERRED_SETUP)) {
|
||||
dstIntent.putExtra(key, srcIntent.getBooleanExtra(key, false));
|
||||
}
|
||||
|
||||
for (String key : Arrays.asList(EXTRA_THEME, EXTRA_SCRIPT_URI, EXTRA_ACTION_ID)) {
|
||||
dstIntent.putExtra(key, srcIntent.getStringExtra(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,6 +217,18 @@ public class WizardManagerHelper {
|
|||
&& originalIntent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an intent is running in "pre-deferred" setup wizard flow.
|
||||
*
|
||||
* @param originalIntent The original intent that was used to start the step, usually via
|
||||
* {@link android.app.Activity#getIntent()}.
|
||||
* @return true if the intent passed in was running in "pre-deferred" setup wizard.
|
||||
*/
|
||||
public static boolean isPreDeferredSetupWizard(Intent originalIntent) {
|
||||
return originalIntent != null
|
||||
&& originalIntent.getBooleanExtra(EXTRA_IS_PRE_DEFERRED_SETUP, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the intent whether the extra indicates that the light theme should be used or not. If
|
||||
* the theme is not specified in the intent, or the theme specified is unknown, the value def
|
||||
|
@ -236,10 +253,12 @@ public class WizardManagerHelper {
|
|||
*/
|
||||
public static boolean isLightTheme(String theme, boolean def) {
|
||||
if (THEME_HOLO_LIGHT.equals(theme) || THEME_MATERIAL_LIGHT.equals(theme)
|
||||
|| THEME_GLIF_LIGHT.equals(theme) || THEME_GLIF_V2_LIGHT.equals(theme)) {
|
||||
|| THEME_GLIF_LIGHT.equals(theme) || THEME_GLIF_V2_LIGHT.equals(theme)
|
||||
|| THEME_GLIF_V3_LIGHT.equals(theme)) {
|
||||
return true;
|
||||
} else if (THEME_HOLO.equals(theme) || THEME_MATERIAL.equals(theme)
|
||||
|| THEME_GLIF.equals(theme) || THEME_GLIF_V2.equals(theme)) {
|
||||
|| THEME_GLIF.equals(theme) || THEME_GLIF_V2.equals(theme)
|
||||
|| THEME_GLIF_V3.equals(theme)) {
|
||||
return false;
|
||||
} else {
|
||||
return def;
|
||||
|
@ -284,6 +303,10 @@ public class WizardManagerHelper {
|
|||
public static @StyleRes int getThemeRes(String theme, @StyleRes int defaultTheme) {
|
||||
if (theme != null) {
|
||||
switch (theme) {
|
||||
case THEME_GLIF_V3_LIGHT:
|
||||
return R.style.SuwThemeGlifV3_Light;
|
||||
case THEME_GLIF_V3:
|
||||
return R.style.SuwThemeGlifV3;
|
||||
case THEME_GLIF_V2_LIGHT:
|
||||
return R.style.SuwThemeGlifV2_Light;
|
||||
case THEME_GLIF_V2:
|
||||
|
|
|
@ -58,6 +58,10 @@ public class CheckableLinearLayout extends LinearLayout implements Checkable {
|
|||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
{
|
||||
setFocusable(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int[] onCreateDrawableState(int extraSpace) {
|
||||
if (mChecked) {
|
||||
|
|
|
@ -137,7 +137,8 @@ public class Illustration extends FrameLayout {
|
|||
if (mAspectRatio != 0.0f) {
|
||||
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int illustrationHeight = (int) (parentWidth / mAspectRatio);
|
||||
illustrationHeight -= illustrationHeight % mBaselineGridSize;
|
||||
illustrationHeight =
|
||||
(int) (illustrationHeight - (illustrationHeight % mBaselineGridSize));
|
||||
setPadding(0, illustrationHeight, 0, 0);
|
||||
}
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
||||
|
|
|
@ -23,9 +23,11 @@ import android.graphics.SurfaceTexture;
|
|||
import android.graphics.drawable.Animatable;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.RawRes;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.view.TextureView;
|
||||
import android.view.View;
|
||||
|
@ -51,8 +53,11 @@ public class IllustrationVideoView extends TextureView implements Animatable,
|
|||
MediaPlayer.OnSeekCompleteListener,
|
||||
MediaPlayer.OnInfoListener {
|
||||
|
||||
private static final String TAG = "IllustrationVideoView";
|
||||
|
||||
protected float mAspectRatio = 1.0f; // initial guess until we know
|
||||
|
||||
@Nullable // Can be null when media player fails to initialize
|
||||
protected MediaPlayer mMediaPlayer;
|
||||
|
||||
private @RawRes int mVideoResId = 0;
|
||||
|
@ -129,15 +134,20 @@ public class IllustrationVideoView extends TextureView implements Animatable,
|
|||
|
||||
mMediaPlayer = MediaPlayer.create(getContext(), mVideoResId);
|
||||
|
||||
mMediaPlayer.setSurface(mSurface);
|
||||
mMediaPlayer.setOnPreparedListener(this);
|
||||
mMediaPlayer.setOnSeekCompleteListener(this);
|
||||
mMediaPlayer.setOnInfoListener(this);
|
||||
if (mMediaPlayer != null) {
|
||||
mMediaPlayer.setSurface(mSurface);
|
||||
mMediaPlayer.setOnPreparedListener(this);
|
||||
mMediaPlayer.setOnSeekCompleteListener(this);
|
||||
mMediaPlayer.setOnInfoListener(this);
|
||||
|
||||
float aspectRatio = (float) mMediaPlayer.getVideoHeight() / mMediaPlayer.getVideoWidth();
|
||||
if (mAspectRatio != aspectRatio) {
|
||||
mAspectRatio = aspectRatio;
|
||||
requestLayout();
|
||||
float aspectRatio =
|
||||
(float) mMediaPlayer.getVideoHeight() / mMediaPlayer.getVideoWidth();
|
||||
if (mAspectRatio != aspectRatio) {
|
||||
mAspectRatio = aspectRatio;
|
||||
requestLayout();
|
||||
}
|
||||
} else {
|
||||
Log.wtf(TAG, "Unable to initialize media player for video view");
|
||||
}
|
||||
if (getWindowVisibility() == View.VISIBLE) {
|
||||
start();
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.setupwizardlib.view;
|
||||
|
||||
import android.text.Selection;
|
||||
import android.text.Spannable;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.method.MovementMethod;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* A movement method that tracks the last result of whether touch events are handled. This is
|
||||
* used to patch the return value of {@link TextView#onTouchEvent} so that it consumes the touch
|
||||
* events only when the movement method says the event is consumed.
|
||||
*/
|
||||
public interface TouchableMovementMethod {
|
||||
|
||||
/**
|
||||
* @return The last touch event received in {@link MovementMethod#onTouchEvent}
|
||||
*/
|
||||
MotionEvent getLastTouchEvent();
|
||||
|
||||
/**
|
||||
* @return The return value of the last {@link MovementMethod#onTouchEvent}, or whether the
|
||||
* last touch event should be considered handled by the text view
|
||||
*/
|
||||
boolean isLastTouchEventHandled();
|
||||
|
||||
/**
|
||||
* An extension of LinkMovementMethod that tracks whether the event is handled when it is
|
||||
* touched.
|
||||
*/
|
||||
class TouchableLinkMovementMethod extends LinkMovementMethod
|
||||
implements TouchableMovementMethod {
|
||||
|
||||
public static TouchableLinkMovementMethod getInstance() {
|
||||
return new TouchableLinkMovementMethod();
|
||||
}
|
||||
|
||||
boolean mLastEventResult = false;
|
||||
MotionEvent mLastEvent;
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
|
||||
mLastEvent = event;
|
||||
boolean result = super.onTouchEvent(widget, buffer, event);
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
// Unfortunately, LinkMovementMethod extends ScrollMovementMethod, and it always
|
||||
// consume the down event. So here we use the selection instead as a hint of whether
|
||||
// the down event landed on a link.
|
||||
mLastEventResult = Selection.getSelectionStart(buffer) != -1;
|
||||
} else {
|
||||
mLastEventResult = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MotionEvent getLastTouchEvent() {
|
||||
return mLastEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLastTouchEventHandled() {
|
||||
return mLastEventResult;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,9 +15,10 @@
|
|||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!-- TODO(yukl): Bump this file to v28 once we can properly test that -->
|
||||
<!-- These styles are only included in the platform build, to make sure that they do not
|
||||
override the corresponding styles in the compatibility build. -->
|
||||
<resources>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- General styles -->
|
||||
|
||||
|
@ -26,6 +27,7 @@
|
|||
Theme.AppCompat. -->
|
||||
<style name="SuwThemeMaterial" parent="android:Theme.Material.NoActionBar">
|
||||
<item name="android:colorAccent">@color/suw_color_accent_dark</item>
|
||||
<item name="android:colorBackground">@color/suw_color_background_dark</item>
|
||||
<item name="android:indeterminateTint">@color/suw_progress_bar_color_dark</item>
|
||||
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
|
||||
<item name="android:indeterminateTintMode">src_in</item>
|
||||
|
@ -34,12 +36,14 @@
|
|||
<item name="android:listPreferredItemPaddingStart">?attr/suwMarginSides</item>
|
||||
<item name="android:navigationBarColor">@android:color/black</item>
|
||||
<item name="android:statusBarColor">@android:color/black</item>
|
||||
<item name="android:textAppearanceListItemSmall">@android:style/TextAppearance.Material.Body1</item>
|
||||
<item name="android:textAppearanceListItemSmall">@style/TextAppearance.SuwItemSummary</item>
|
||||
<item name="android:textColorLink">@color/suw_link_color_dark</item>
|
||||
<item name="android:windowAnimationStyle">@style/Animation.SuwWindowAnimation</item>
|
||||
<item name="android:windowDisablePreview">true</item>
|
||||
<item name="android:windowSoftInputMode">adjustResize</item>
|
||||
|
||||
<item name="suwButtonAllCaps">true</item>
|
||||
<item name="suwButtonFontFamily">sans-serif</item>
|
||||
<item name="suwCardBackground">@drawable/suw_card_bg</item>
|
||||
<item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
|
||||
<item name="suwDividerInsetEnd">0dp</item>
|
||||
|
@ -54,6 +58,7 @@
|
|||
|
||||
<style name="SuwThemeMaterial.Light" parent="android:Theme.Material.Light.NoActionBar">
|
||||
<item name="android:colorAccent">@color/suw_color_accent_light</item>
|
||||
<item name="android:colorBackground">@color/suw_color_background_light</item>
|
||||
<item name="android:indeterminateTint">@color/suw_progress_bar_color_light</item>
|
||||
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
|
||||
<item name="android:indeterminateTintMode">src_in</item>
|
||||
|
@ -62,12 +67,14 @@
|
|||
<item name="android:listPreferredItemPaddingStart">?attr/suwMarginSides</item>
|
||||
<item name="android:navigationBarColor">@android:color/black</item>
|
||||
<item name="android:statusBarColor">@android:color/black</item>
|
||||
<item name="android:textAppearanceListItemSmall">@android:style/TextAppearance.Material.Body1</item>
|
||||
<item name="android:textAppearanceListItemSmall">@style/TextAppearance.SuwItemSummary</item>
|
||||
<item name="android:textColorLink">@color/suw_link_color_light</item>
|
||||
<item name="android:windowAnimationStyle">@style/Animation.SuwWindowAnimation</item>
|
||||
<item name="android:windowDisablePreview">true</item>
|
||||
<item name="android:windowSoftInputMode">adjustResize</item>
|
||||
|
||||
<item name="suwButtonAllCaps">true</item>
|
||||
<item name="suwButtonFontFamily">sans-serif</item>
|
||||
<item name="suwCardBackground">@drawable/suw_card_bg</item>
|
||||
<item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
|
||||
<item name="suwDividerInsetEnd">0dp</item>
|
||||
|
@ -84,7 +91,7 @@
|
|||
<style name="SuwThemeGlif" parent="android:Theme.Material.NoActionBar">
|
||||
<item name="android:colorAccent">@color/suw_color_accent_glif_dark</item>
|
||||
<item name="android:colorBackground">@color/suw_glif_background_color_dark</item>
|
||||
<item name="android:colorPrimary">@color/suw_color_accent_glif_dark</item>
|
||||
<item name="android:colorPrimary">?android:attr/colorAccent</item>
|
||||
<item name="android:indeterminateTint">?android:attr/colorPrimary</item>
|
||||
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
|
||||
<item name="android:indeterminateTintMode">src_in</item>
|
||||
|
@ -100,12 +107,16 @@
|
|||
<item name="android:windowDisablePreview">true</item>
|
||||
<item name="android:windowSoftInputMode">adjustResize</item>
|
||||
|
||||
<item name="suwButtonAllCaps">true</item>
|
||||
<item name="suwButtonCornerRadius">@dimen/suw_glif_button_corner_radius</item>
|
||||
<item name="suwButtonFontFamily">sans-serif</item>
|
||||
<item name="suwColorPrimary">?android:attr/colorPrimary</item>
|
||||
<item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
|
||||
<item name="suwDividerInsetEnd">0dp</item>
|
||||
<item name="suwDividerInsetStart">@dimen/suw_items_glif_icon_divider_inset</item>
|
||||
<item name="suwDividerInsetStartNoIcon">@dimen/suw_items_glif_text_divider_inset</item>
|
||||
<item name="suwGlifHeaderGravity">start</item>
|
||||
<item name="suwGlifIconStyle">@style/SuwGlifIcon</item>
|
||||
<item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description.Glif</item>
|
||||
<item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle.GlifDescription</item>
|
||||
<item name="suwListItemIconColor">@color/suw_list_item_icon_color_dark</item>
|
||||
|
@ -116,7 +127,7 @@
|
|||
<style name="SuwThemeGlif.Light" parent="android:Theme.Material.Light.NoActionBar">
|
||||
<item name="android:colorAccent">@color/suw_color_accent_glif_light</item>
|
||||
<item name="android:colorBackground">@color/suw_glif_background_color_light</item>
|
||||
<item name="android:colorPrimary">@color/suw_color_accent_glif_light</item>
|
||||
<item name="android:colorPrimary">?android:attr/colorAccent</item>
|
||||
<item name="android:indeterminateTint">?android:attr/colorPrimary</item>
|
||||
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
|
||||
<item name="android:indeterminateTintMode">src_in</item>
|
||||
|
@ -132,12 +143,16 @@
|
|||
<item name="android:windowDisablePreview">true</item>
|
||||
<item name="android:windowSoftInputMode">adjustResize</item>
|
||||
|
||||
<item name="suwButtonAllCaps">true</item>
|
||||
<item name="suwButtonCornerRadius">@dimen/suw_glif_button_corner_radius</item>
|
||||
<item name="suwButtonFontFamily">sans-serif</item>
|
||||
<item name="suwColorPrimary">?android:attr/colorPrimary</item>
|
||||
<item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
|
||||
<item name="suwDividerInsetEnd">0dp</item>
|
||||
<item name="suwDividerInsetStart">@dimen/suw_items_glif_icon_divider_inset</item>
|
||||
<item name="suwDividerInsetStartNoIcon">@dimen/suw_items_glif_text_divider_inset</item>
|
||||
<item name="suwGlifHeaderGravity">start</item>
|
||||
<item name="suwGlifIconStyle">@style/SuwGlifIcon</item>
|
||||
<item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description.Glif</item>
|
||||
<item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle.GlifDescription</item>
|
||||
<item name="suwListItemIconColor">@color/suw_list_item_icon_color_light</item>
|
||||
|
@ -145,6 +160,27 @@
|
|||
<item name="suwScrollIndicators">bottom</item>
|
||||
</style>
|
||||
|
||||
<style name="SuwThemeGlifV3" parent="SuwThemeGlifV2">
|
||||
<item name="android:colorAccent">@color/suw_color_accent_glif_v3</item>
|
||||
|
||||
<item name="suwButtonAllCaps">false</item>
|
||||
<item name="suwButtonCornerRadius">@dimen/suw_glif_v3_button_corner_radius</item>
|
||||
<item name="suwButtonFontFamily">@string/suwFontSecondaryMedium</item>
|
||||
</style>
|
||||
|
||||
<style name="SuwThemeGlifV3.Light" parent="SuwThemeGlifV2.Light">
|
||||
<item name="android:colorAccent">@color/suw_color_accent_glif_v3</item>
|
||||
<item name="android:navigationBarColor">@color/suw_glif_v3_nav_bar_color_light</item>
|
||||
<!-- Ignore NewApi: For some reason lint seems to think this API is new in v28 (b/73514594) -->
|
||||
<item name="android:navigationBarDividerColor" tools:ignore="NewApi">@color/suw_glif_v3_nav_bar_divider_color_light</item>
|
||||
<!-- Ignore NewApi: For some reason lint seems to think this API is new in v28 (b/73514594) -->
|
||||
<item name="android:windowLightNavigationBar" tools:ignore="NewApi">true</item>
|
||||
|
||||
<item name="suwButtonAllCaps">false</item>
|
||||
<item name="suwButtonCornerRadius">@dimen/suw_glif_v3_button_corner_radius</item>
|
||||
<item name="suwButtonFontFamily">@string/suwFontSecondaryMedium</item>
|
||||
</style>
|
||||
|
||||
<!-- Button styles -->
|
||||
|
||||
<style name="SuwGlifButton.Primary" parent="android:Widget.Material.Button.Colored">
|
||||
|
@ -154,8 +190,13 @@
|
|||
<item name="android:buttonStyle">@style/SuwGlifButton.Primary</item>
|
||||
|
||||
<!-- Values used in styles -->
|
||||
<item name="android:fontFamily">?attr/suwButtonFontFamily</item>
|
||||
<item name="android:paddingLeft">@dimen/suw_glif_button_padding</item>
|
||||
<item name="android:paddingRight">@dimen/suw_glif_button_padding</item>
|
||||
<item name="android:textAllCaps">?attr/suwButtonAllCaps</item>
|
||||
|
||||
<!-- Values used in themes -->
|
||||
<item name="android:buttonCornerRadius" tools:ignore="NewApi">?attr/suwButtonCornerRadius</item>
|
||||
</style>
|
||||
|
||||
<style name="SuwGlifButton.Secondary" parent="android:Widget.Material.Button.Borderless.Colored">
|
||||
|
@ -166,11 +207,14 @@
|
|||
<item name="android:theme">@style/SuwGlifButton.Secondary</item>
|
||||
|
||||
<!-- Values used in styles -->
|
||||
<item name="android:fontFamily">?attr/suwButtonFontFamily</item>
|
||||
<item name="android:minWidth">0dp</item>
|
||||
<item name="android:paddingLeft">@dimen/suw_glif_button_padding</item>
|
||||
<item name="android:paddingRight">@dimen/suw_glif_button_padding</item>
|
||||
<item name="android:textAllCaps">?attr/suwButtonAllCaps</item>
|
||||
|
||||
<!-- Values used in themes -->
|
||||
<item name="android:buttonCornerRadius" tools:ignore="NewApi">?attr/suwButtonCornerRadius</item>
|
||||
<item name="android:colorControlHighlight">@color/suw_flat_button_highlight</item>
|
||||
</style>
|
||||
|
|
@ -20,16 +20,18 @@ import android.content.Context;
|
|||
import android.text.Annotation;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.method.MovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.TextAppearanceSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.setupwizardlib.span.LinkSpan;
|
||||
import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener;
|
||||
import com.android.setupwizardlib.span.SpanHelper;
|
||||
import com.android.setupwizardlib.view.TouchableMovementMethod.TouchableLinkMovementMethod;
|
||||
|
||||
/**
|
||||
* An extension of TextView that automatically replaces the annotation tags as specified in
|
||||
|
@ -112,7 +114,7 @@ public class RichTextView extends TextView implements OnLinkClickListener {
|
|||
// nullifying any return values of MovementMethod.onTouchEvent.
|
||||
// To still allow propagating touch events to the parent when this view doesn't have
|
||||
// links, we only set the movement method here if the text contains links.
|
||||
setMovementMethod(LinkMovementMethod.getInstance());
|
||||
setMovementMethod(TouchableLinkMovementMethod.getInstance());
|
||||
} else {
|
||||
setMovementMethod(null);
|
||||
}
|
||||
|
@ -121,6 +123,11 @@ public class RichTextView extends TextView implements OnLinkClickListener {
|
|||
// as individual TextViews consume touch events and thereby reducing the focus window
|
||||
// shown by Talkback. Disable focus if there are no links
|
||||
setFocusable(hasLinks);
|
||||
// Do not "reveal" (i.e. scroll to) this view when this view is focused. Since this view is
|
||||
// focusable in touch mode, we may be focused when the screen is first shown, and starting
|
||||
// a screen halfway scrolled down is confusing to the user.
|
||||
setRevealOnFocusHint(false);
|
||||
setFocusableInTouchMode(hasLinks);
|
||||
}
|
||||
|
||||
private boolean hasLinks(CharSequence text) {
|
||||
|
@ -132,6 +139,25 @@ public class RichTextView extends TextView implements OnLinkClickListener {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("ClickableViewAccessibility") // super.onTouchEvent is called
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
// Since View#onTouchEvent always return true if the view is clickable (which is the case
|
||||
// when a TextView has a movement method), override the implementation to allow the movement
|
||||
// method, if it implements TouchableMovementMethod, to say that the touch is not handled,
|
||||
// allowing the event to bubble up to the parent view.
|
||||
boolean superResult = super.onTouchEvent(event);
|
||||
MovementMethod movementMethod = getMovementMethod();
|
||||
if (movementMethod instanceof TouchableMovementMethod) {
|
||||
TouchableMovementMethod touchableMovementMethod =
|
||||
(TouchableMovementMethod) movementMethod;
|
||||
if (touchableMovementMethod.getLastTouchEvent() == event) {
|
||||
return touchableMovementMethod.isLastTouchEventHandled();
|
||||
}
|
||||
}
|
||||
return superResult;
|
||||
}
|
||||
|
||||
public void setOnLinkClickListener(OnLinkClickListener listener) {
|
||||
mOnLinkClickListener = listener;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,11 @@
|
|||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/suw_layout_sticky_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<!-- Ignore UnusedAttribute: scrollIndicators is new in M. Default to no indicators in older
|
||||
versions. -->
|
||||
<com.android.setupwizardlib.view.HeaderRecyclerView
|
||||
|
|
|
@ -107,10 +107,12 @@ public class GlifRecyclerLayout extends GlifLayout {
|
|||
}
|
||||
|
||||
@Override
|
||||
public View findManagedViewById(int id) {
|
||||
// Returning generic type is the common pattern used for findViewBy* methods
|
||||
@SuppressWarnings("TypeParameterUnusedInFormals")
|
||||
public <T extends View> T findManagedViewById(int id) {
|
||||
final View header = mRecyclerMixin.getHeader();
|
||||
if (header != null) {
|
||||
final View view = header.findViewById(id);
|
||||
final T view = header.findViewById(id);
|
||||
if (view != null) {
|
||||
return view;
|
||||
}
|
||||
|
|
|
@ -127,10 +127,12 @@ public class SetupWizardRecyclerLayout extends SetupWizardLayout {
|
|||
}
|
||||
|
||||
@Override
|
||||
public View findManagedViewById(int id) {
|
||||
// Returning generic type is the common pattern used for findViewBy* methods
|
||||
@SuppressWarnings("TypeParameterUnusedInFormals")
|
||||
public <T extends View> T findManagedViewById(int id) {
|
||||
final View header = mRecyclerMixin.getHeader();
|
||||
if (header != null) {
|
||||
final View view = header.findViewById(id);
|
||||
final T view = header.findViewById(id);
|
||||
if (view != null) {
|
||||
return view;
|
||||
}
|
||||
|
|
|
@ -143,9 +143,9 @@ public class RecyclerItemAdapter extends RecyclerView.Adapter<ItemViewHolder>
|
|||
@Override
|
||||
public void onBindViewHolder(ItemViewHolder holder, int position) {
|
||||
final IItem item = getItem(position);
|
||||
item.onBindView(holder.itemView);
|
||||
holder.setEnabled(item.isEnabled());
|
||||
holder.setItem(item);
|
||||
item.onBindView(holder.itemView);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,7 +27,6 @@ import static org.robolectric.RuntimeEnvironment.application;
|
|||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.RecyclerView.OnScrollListener;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
|
@ -38,7 +37,7 @@ import org.mockito.Mock;
|
|||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
public class RecyclerViewScrollHandlingDelegateTest {
|
||||
|
||||
|
|
|
@ -13,6 +13,23 @@ android {
|
|||
|
||||
publishNonDefault true
|
||||
|
||||
flavorDimensions 'compat'
|
||||
|
||||
productFlavors {
|
||||
// DEPRECATED: Platform version that will not include the compatibility libraries
|
||||
platformDeprecated {
|
||||
dimension 'compat'
|
||||
// TODO(yukl): Bump this file to v28 once we can properly test that
|
||||
minSdkVersion 27
|
||||
}
|
||||
|
||||
// Provides backwards compatibility for Gingerbread or above, using support libraries.
|
||||
gingerbreadCompat {
|
||||
dimension 'compat'
|
||||
minSdkVersion 9
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'main/AndroidManifest.xml'
|
||||
|
@ -21,43 +38,7 @@ android {
|
|||
res.srcDirs = ['main/res']
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
// Platform version that will not include the compatibility libraries
|
||||
platform {
|
||||
minSdkVersion 23
|
||||
|
||||
dependencies {
|
||||
// Read the dependencies from the "deps" map in the extra properties.
|
||||
//
|
||||
// For builds in the Android tree we want to build the dependencies from source
|
||||
// for reproducible builds, for example in build.gradle define something like
|
||||
// this:
|
||||
// ext {
|
||||
// deps = ['project-name': project(':project-path')]
|
||||
// }
|
||||
//
|
||||
// For standalone project clients, since the source may not be available, we
|
||||
// fetch the dependencies from maven. For example in standalone.gradle define
|
||||
// something like this:
|
||||
// ext {
|
||||
// deps = ['project-name': 'com.example.group:project-name:1.0.0']
|
||||
// }
|
||||
//
|
||||
platformCompile deps['support-annotations']
|
||||
}
|
||||
}
|
||||
|
||||
// Provides backwards compatibility for Gingerbread or above, using support libraries.
|
||||
gingerbreadCompat {
|
||||
minSdkVersion 9
|
||||
dependencies {
|
||||
gingerbreadCompatCompile deps['support-appcompat-v7']
|
||||
gingerbreadCompatCompile deps['support-recyclerview-v7']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
platform {
|
||||
platformDeprecated {
|
||||
java.srcDirs = ['platform/src']
|
||||
res.srcDirs = ['platform/res']
|
||||
}
|
||||
|
@ -68,3 +49,26 @@ android {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Read the dependencies from the "deps" map in the extra properties.
|
||||
//
|
||||
// For builds in the Android tree we want to build the dependencies from source
|
||||
// for reproducible builds, for example in build.gradle define something like
|
||||
// this:
|
||||
// ext {
|
||||
// deps = ['project-name': project(':project-path')]
|
||||
// }
|
||||
//
|
||||
// For standalone project clients, since the source may not be available, we
|
||||
// fetch the dependencies from maven. For example in standalone.gradle define
|
||||
// something like this:
|
||||
// ext {
|
||||
// deps = ['project-name': 'com.example.group:project-name:1.0.0']
|
||||
// }
|
||||
//
|
||||
platformDeprecatedCompile deps['support-annotations']
|
||||
|
||||
gingerbreadCompatCompile deps['support-appcompat-v7']
|
||||
gingerbreadCompatCompile deps['support-recyclerview-v7']
|
||||
}
|
||||
|
|
|
@ -5,6 +5,18 @@ apply from: 'standalone-rules.gradle'
|
|||
apply from: '../tools/gradle/dist-library-instrumentation-tests.gradle'
|
||||
apply from: '../tools/gradle/dist-unit-tests.gradle'
|
||||
|
||||
apply plugin: 'net.ltgt.errorprone'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url "$rootDir/prebuilts/tools/common/m2/repository" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
|
||||
}
|
||||
}
|
||||
|
||||
// Add targets for tests
|
||||
android.sourceSets {
|
||||
androidTest {
|
||||
|
@ -13,16 +25,17 @@ android.sourceSets {
|
|||
res.srcDirs = ['test/instrumentation/res']
|
||||
|
||||
dependencies {
|
||||
androidTestCompile 'com.android.support.test:rules:0.5'
|
||||
androidTestCompile 'com.android.support.test:runner:0.5'
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
|
||||
androidTestCompile 'junit:junit:4.+'
|
||||
androidTestCompile 'org.mockito:mockito-core:1.9.5'
|
||||
androidTestImplementation 'com.android.support.test:rules:1.0.0'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.0'
|
||||
androidTestImplementation 'com.google.dexmaker:dexmaker:1.2'
|
||||
androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:1.2'
|
||||
androidTestImplementation 'com.google.truth:truth:0.31'
|
||||
androidTestImplementation 'junit:junit:4.+'
|
||||
androidTestImplementation 'org.mockito:mockito-core:1.9.5'
|
||||
}
|
||||
}
|
||||
|
||||
androidTestPlatform {
|
||||
androidTestPlatformDeprecated {
|
||||
java.srcDirs = ['platform/test/src']
|
||||
}
|
||||
|
||||
|
@ -38,12 +51,13 @@ android.sourceSets {
|
|||
java.srcDirs = ['test/robotest/src']
|
||||
|
||||
dependencies {
|
||||
testCompile 'org.robolectric:robolectric:3.+'
|
||||
testCompile 'org.robolectric:shadows-core:3.+'
|
||||
testCompile 'junit:junit:4.+'
|
||||
testCompile 'org.mockito:mockito-core:1.9.5'
|
||||
testImplementation 'org.robolectric:robolectric:3.6.1'
|
||||
testImplementation 'org.robolectric:shadows-framework:3.6.1'
|
||||
testImplementation 'junit:junit:4.+'
|
||||
testImplementation 'com.google.truth:truth:0.31'
|
||||
testImplementation 'org.mockito:mockito-core:1.9.5'
|
||||
// Workaround for https://github.com/robolectric/robolectric/issues/2566
|
||||
testCompile 'org.khronos:opengl-api:gl1.1-android-2.1_r1'
|
||||
testImplementation 'org.khronos:opengl-api:gl1.1-android-2.1_r1'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,6 +65,9 @@ android.sourceSets {
|
|||
java.srcDirs = ['gingerbread/test/robotest/src', 'recyclerview/test/robotest/src']
|
||||
}
|
||||
}
|
||||
|
||||
android.testOptions.unitTests.includeAndroidResources = true
|
||||
|
||||
android.defaultConfig.testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
android.lintOptions {
|
||||
abortOnError true
|
||||
|
@ -64,14 +81,3 @@ android.lintOptions {
|
|||
android.libraryVariants.all { variant ->
|
||||
variant.assemble.dependsOn(tasks.findByName('lint'))
|
||||
}
|
||||
|
||||
// For compatibility with existing continuous test configurations, copy the file to
|
||||
// setup-wizard-libTest.apk
|
||||
// TODO: Remove this once continuous test configurations are updated to handle the new file name
|
||||
task createLegacyTestApk(type: Copy) {
|
||||
from "${project.ext.distDir}/setup-wizard-lib-gingerbreadCompat-debug-androidTest.apk"
|
||||
into "${project.ext.distDir}"
|
||||
rename ('setup-wizard-lib-gingerbreadCompat-debug-androidTest.apk', 'setup-wizard-libTest.apk')
|
||||
}
|
||||
|
||||
tasks.dist.finalizedBy createLegacyTestApk
|
||||
|
|
|
@ -16,5 +16,5 @@
|
|||
|
||||
apply from: 'standalone-rules.gradle'
|
||||
|
||||
android.compileSdkVersion 25
|
||||
android.buildToolsVersion '25.0.0'
|
||||
android.compileSdkVersion 26
|
||||
android.buildToolsVersion '26.0.0'
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.android.setupwizardlib.test">
|
||||
|
||||
<uses-sdk tools:overrideLibrary="android.support.test,android.app,android.support.test.rule" />
|
||||
<uses-sdk tools:overrideLibrary="android.support.test.rules,android.support.test.runner" />
|
||||
|
||||
<application>
|
||||
<activity android:name=".util.DrawingTestActivity" />
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package com.android.setupwizardlib.template;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.Matchers.eq;
|
||||
|
@ -27,10 +29,12 @@ import android.content.res.XmlResourceParser;
|
|||
import android.graphics.Color;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.Xml;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.setupwizardlib.TemplateLayout;
|
||||
|
@ -74,6 +78,26 @@ public class IconMixinTest {
|
|||
mixin.setIcon(drawable);
|
||||
|
||||
assertSame(drawable, mIconView.getDrawable());
|
||||
assertEquals(View.VISIBLE, mIconView.getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setIcon_resourceId_shouldSetIcon() {
|
||||
int icon = android.R.drawable.ic_menu_add;
|
||||
IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
|
||||
mixin.setIcon(icon);
|
||||
|
||||
Drawable drawable = mIconView.getDrawable();
|
||||
assertThat(drawable).isInstanceOf(BitmapDrawable.class);
|
||||
assertEquals(View.VISIBLE, mIconView.getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setIcon_shouldSetVisibilityToGone_whenIconIsNull() {
|
||||
IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
|
||||
mixin.setIcon(null);
|
||||
|
||||
assertEquals(View.GONE, mIconView.getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -101,5 +125,20 @@ public class IconMixinTest {
|
|||
.getDrawable(android.R.drawable.ic_menu_add);
|
||||
final BitmapDrawable actual = (BitmapDrawable) mIconView.getDrawable();
|
||||
assertEquals(expected.getBitmap(), actual.getBitmap());
|
||||
assertEquals(View.VISIBLE, mIconView.getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setContentDescription_shouldSetContentDescriptionOnIconView() {
|
||||
IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
|
||||
mixin.setContentDescription("hello world");
|
||||
assertThat(mIconView.getContentDescription()).isEqualTo("hello world");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getContentDescription_shouldReturnContentDescriptionFromView() {
|
||||
IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
|
||||
mIconView.setContentDescription("aloha");
|
||||
assertThat(mixin.getContentDescription()).isEqualTo("aloha");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ public class GlifPatternDrawableTest {
|
|||
assertEquals("Matrices should match", expected, canvas.getMatrix());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@Test
|
||||
public void testScaleToCanvasMaxSize() {
|
||||
final Canvas canvas = new Canvas();
|
||||
final Matrix expected = new Matrix(canvas.getMatrix());
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package com.android.setupwizardlib.test;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
@ -31,6 +33,7 @@ import android.support.test.annotation.UiThreadTest;
|
|||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.rule.UiThreadTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
@ -131,7 +134,9 @@ public class SystemBarHelperTest {
|
|||
@Test
|
||||
public void testShowSystemBarsWindow() {
|
||||
final Window window = createWindowWithSystemUiVisibility(0x456);
|
||||
SystemBarHelper.showSystemBars(window, InstrumentationRegistry.getContext());
|
||||
Context context = new ContextThemeWrapper(
|
||||
InstrumentationRegistry.getContext(), android.R.style.Theme);
|
||||
SystemBarHelper.showSystemBars(window, context);
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
||||
assertEquals(
|
||||
"DEFAULT_IMMERSIVE_FLAGS should be removed from window's systemUiVisibility",
|
||||
|
@ -191,11 +196,15 @@ public class SystemBarHelperTest {
|
|||
@UiThreadTest
|
||||
@Test
|
||||
public void testSetBackButtonVisibleTrue() {
|
||||
final Window window = createWindowWithSystemUiVisibility(0x456);
|
||||
final Window window = createWindowWithSystemUiVisibility(STATUS_BAR_DISABLE_BACK | 0x456);
|
||||
SystemBarHelper.setBackButtonVisible(window, true);
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
|
||||
assertEquals("View visibility should be 0x456", 0x456,
|
||||
window.getAttributes().systemUiVisibility);
|
||||
assertThat(window.getAttributes().systemUiVisibility)
|
||||
.named("window sysUiVisibility")
|
||||
.isEqualTo(0x456);
|
||||
assertThat(window.getDecorView().getSystemUiVisibility())
|
||||
.named("decor view sysUiVisibility")
|
||||
.isEqualTo(0x456);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,8 +214,12 @@ public class SystemBarHelperTest {
|
|||
final Window window = createWindowWithSystemUiVisibility(0x456);
|
||||
SystemBarHelper.setBackButtonVisible(window, false);
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
|
||||
assertEquals("STATUS_BAR_DISABLE_BACK should be added to systemUiVisibility",
|
||||
0x456 | STATUS_BAR_DISABLE_BACK, window.getAttributes().systemUiVisibility);
|
||||
assertThat(window.getAttributes().systemUiVisibility)
|
||||
.named("window sysUiVisibility")
|
||||
.isEqualTo(0x456 | STATUS_BAR_DISABLE_BACK);
|
||||
assertThat(window.getDecorView().getSystemUiVisibility())
|
||||
.named("decor view sysUiVisibility")
|
||||
.isEqualTo(0x456 | STATUS_BAR_DISABLE_BACK);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ import android.graphics.Color;
|
|||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.View;
|
||||
|
@ -53,7 +55,7 @@ import org.robolectric.Robolectric;
|
|||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
public class GlifLayoutTest {
|
||||
|
||||
private Context mContext;
|
||||
|
@ -266,6 +268,74 @@ public class GlifLayoutTest {
|
|||
assertNotNull(layout.findViewById(android.R.id.text1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inflateStickyHeader_shouldAddViewToLayout() {
|
||||
GlifLayout layout = new GlifLayout(mContext);
|
||||
|
||||
final View view = layout.inflateStickyHeader(android.R.layout.simple_list_item_1);
|
||||
assertEquals(android.R.id.text1, view.getId());
|
||||
assertNotNull(layout.findViewById(android.R.id.text1));
|
||||
}
|
||||
|
||||
@Config(qualifiers = "sw600dp")
|
||||
@Test
|
||||
public void inflateStickyHeader_whenOnTablets_shouldAddViewToLayout() {
|
||||
inflateStickyHeader_shouldAddViewToLayout();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inflateStickyHeader_whenInXml_shouldAddViewToLayout() {
|
||||
GlifLayout layout = new GlifLayout(
|
||||
mContext,
|
||||
Robolectric.buildAttributeSet()
|
||||
.addAttribute(R.attr.suwStickyHeader, "@android:layout/simple_list_item_1")
|
||||
.build());
|
||||
|
||||
assertNotNull(layout.findViewById(android.R.id.text1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inflateStickyHeader_whenOnBlankTemplate_shouldAddViewToLayout() {
|
||||
GlifLayout layout = new GlifLayout(mContext, R.layout.suw_glif_blank_template);
|
||||
|
||||
final View view = layout.inflateStickyHeader(android.R.layout.simple_list_item_1);
|
||||
assertEquals(android.R.id.text1, view.getId());
|
||||
assertNotNull(layout.findViewById(android.R.id.text1));
|
||||
}
|
||||
|
||||
@Config(qualifiers = "sw600dp")
|
||||
@Test
|
||||
public void inflateStickyHeader_whenOnBlankTemplateTablet_shouldAddViewToLayout() {
|
||||
inflateStickyHeader_whenOnBlankTemplate_shouldAddViewToLayout();
|
||||
}
|
||||
|
||||
@Config(minSdk = Config.OLDEST_SDK, maxSdk = Config.NEWEST_SDK)
|
||||
@Test
|
||||
public void createFromXml_shouldSetLayoutFullscreen_whenLayoutFullscreenIsNotSet() {
|
||||
GlifLayout layout = new GlifLayout(
|
||||
mContext,
|
||||
Robolectric.buildAttributeSet()
|
||||
.build());
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.M) {
|
||||
assertEquals(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,
|
||||
layout.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromXml_shouldNotSetLayoutFullscreen_whenLayoutFullscreenIsFalse() {
|
||||
GlifLayout layout = new GlifLayout(
|
||||
mContext,
|
||||
Robolectric.buildAttributeSet()
|
||||
.addAttribute(R.attr.suwLayoutFullscreen, "false")
|
||||
.build());
|
||||
|
||||
assertEquals(
|
||||
0,
|
||||
layout.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
|
||||
private Drawable getPhoneBackground(GlifLayout layout) {
|
||||
final StatusBarBackgroundLayout patternBg =
|
||||
(StatusBarBackgroundLayout) layout.findManagedViewById(R.id.suw_pattern_bg);
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.setupwizardlib.gesture;
|
||||
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.robolectric.RuntimeEnvironment.application;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
public class ConsecutiveTapsGestureDetectorTest {
|
||||
|
||||
@Mock
|
||||
private ConsecutiveTapsGestureDetector.OnConsecutiveTapsListener mListener;
|
||||
|
||||
private ConsecutiveTapsGestureDetector mDetector;
|
||||
private int mSlop;
|
||||
private int mTapTimeout;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
View view = new View(application);
|
||||
view.measure(500, 500);
|
||||
view.layout(0, 0, 500, 500);
|
||||
mDetector = new ConsecutiveTapsGestureDetector(mListener, view);
|
||||
|
||||
mSlop = ViewConfiguration.get(application).getScaledDoubleTapSlop();
|
||||
mTapTimeout = ViewConfiguration.getDoubleTapTimeout();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTouchEvent_shouldTriggerCallbackOnFourTaps() {
|
||||
InOrder inOrder = inOrder(mListener);
|
||||
|
||||
tap(0, 25f, 25f);
|
||||
inOrder.verify(mListener).onConsecutiveTaps(eq(1));
|
||||
|
||||
tap(100, 25f, 25f);
|
||||
inOrder.verify(mListener).onConsecutiveTaps(eq(2));
|
||||
|
||||
tap(200, 25f, 25f);
|
||||
inOrder.verify(mListener).onConsecutiveTaps(eq(3));
|
||||
|
||||
tap(300, 25f, 25f);
|
||||
inOrder.verify(mListener).onConsecutiveTaps(eq(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTouchEvent_tapOnDifferentLocation_shouldResetCounter() {
|
||||
InOrder inOrder = inOrder(mListener);
|
||||
|
||||
tap(0, 25f, 25f);
|
||||
inOrder.verify(mListener).onConsecutiveTaps(eq(1));
|
||||
|
||||
tap(100, 25f, 25f);
|
||||
inOrder.verify(mListener).onConsecutiveTaps(eq(2));
|
||||
|
||||
tap(200, 25f + mSlop * 2, 25f);
|
||||
inOrder.verify(mListener).onConsecutiveTaps(eq(1));
|
||||
|
||||
tap(300, 25f + mSlop * 2, 25f);
|
||||
inOrder.verify(mListener).onConsecutiveTaps(eq(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTouchEvent_tapAfterTimeout_shouldResetCounter() {
|
||||
InOrder inOrder = inOrder(mListener);
|
||||
|
||||
tap(0, 25f, 25f);
|
||||
inOrder.verify(mListener).onConsecutiveTaps(eq(1));
|
||||
|
||||
tap(100, 25f, 25f);
|
||||
inOrder.verify(mListener).onConsecutiveTaps(eq(2));
|
||||
|
||||
tap(200 + mTapTimeout, 25f, 25f);
|
||||
inOrder.verify(mListener).onConsecutiveTaps(eq(1));
|
||||
|
||||
tap(300 + mTapTimeout, 25f, 25f);
|
||||
inOrder.verify(mListener).onConsecutiveTaps(eq(2));
|
||||
}
|
||||
|
||||
private void tap(int timeMillis, float x, float y) {
|
||||
mDetector.onTouchEvent(
|
||||
MotionEvent.obtain(timeMillis, timeMillis, MotionEvent.ACTION_DOWN, x, y, 0));
|
||||
mDetector.onTouchEvent(
|
||||
MotionEvent.obtain(timeMillis, timeMillis + 10, MotionEvent.ACTION_UP, x, y, 0));
|
||||
}
|
||||
}
|
|
@ -39,7 +39,6 @@ import android.widget.Button;
|
|||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.R;
|
||||
import com.android.setupwizardlib.items.ButtonItem.OnClickListener;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
@ -50,9 +49,7 @@ import org.junit.runner.RunWith;
|
|||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(
|
||||
constants = BuildConfig.class,
|
||||
sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
public class ButtonItemTest {
|
||||
|
||||
private ViewGroup mParent;
|
||||
|
|
|
@ -24,7 +24,6 @@ import static org.mockito.Mockito.inOrder;
|
|||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
|
@ -36,9 +35,7 @@ import org.mockito.MockitoAnnotations;
|
|||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(
|
||||
constants = BuildConfig.class,
|
||||
sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
public class ItemGroupTest {
|
||||
|
||||
private static final Item CHILD_1 = new EqualsItem("Child 1");
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.setupwizardlib.robolectric;
|
||||
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.internal.GradleManifestFactory;
|
||||
import org.robolectric.internal.ManifestIdentifier;
|
||||
import org.robolectric.res.FileFsFile;
|
||||
import org.robolectric.util.Logger;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Modified GradleManifestFactory to patch an issue where some build variants have merged
|
||||
* resources under res/merged/variant/type while others have it under bundles/variant/type/res.
|
||||
*
|
||||
* The change is that in the .exists() checks below we check for specific folders for the build
|
||||
* variant rather than checking existence at the parent directory.
|
||||
*/
|
||||
class PatchedGradleManifestFactory extends GradleManifestFactory {
|
||||
|
||||
@Override
|
||||
public ManifestIdentifier identify(Config config) {
|
||||
if (config.constants() == Void.class) {
|
||||
Logger.error("Field 'constants' not specified in @Config annotation");
|
||||
Logger.error("This is required when using Robolectric with Gradle!");
|
||||
throw new RuntimeException("No 'constants' field in @Config annotation!");
|
||||
}
|
||||
|
||||
final String buildOutputDir = getBuildOutputDir(config);
|
||||
final String type = getType(config);
|
||||
final String flavor = getFlavor(config);
|
||||
final String abiSplit = getAbiSplit(config);
|
||||
final String packageName = config.packageName().isEmpty()
|
||||
? config.constants().getPackage().getName()
|
||||
: config.packageName();
|
||||
|
||||
final FileFsFile res;
|
||||
final FileFsFile assets;
|
||||
final FileFsFile manifest;
|
||||
|
||||
if (FileFsFile.from(buildOutputDir, "data-binding-layout-out", flavor, type).exists()) {
|
||||
// Android gradle plugin 1.5.0+ puts the merged layouts in data-binding-layout-out.
|
||||
// https://github.com/robolectric/robolectric/issues/2143
|
||||
res = FileFsFile.from(buildOutputDir, "data-binding-layout-out", flavor, type);
|
||||
} else if (FileFsFile.from(buildOutputDir, "res", "merged", flavor, type).exists()) {
|
||||
// res/merged added in Android Gradle plugin 1.3-beta1
|
||||
res = FileFsFile.from(buildOutputDir, "res", "merged", flavor, type);
|
||||
} else if (FileFsFile.from(buildOutputDir, "res", flavor, type).exists()) {
|
||||
res = FileFsFile.from(buildOutputDir, "res", flavor, type);
|
||||
} else {
|
||||
res = FileFsFile.from(buildOutputDir, "bundles", flavor, type, "res");
|
||||
}
|
||||
|
||||
if (FileFsFile.from(buildOutputDir, "assets", flavor, type).exists()) {
|
||||
assets = FileFsFile.from(buildOutputDir, "assets", flavor, type);
|
||||
} else {
|
||||
assets = FileFsFile.from(buildOutputDir, "bundles", flavor, type, "assets");
|
||||
}
|
||||
|
||||
String manifestName = config.manifest();
|
||||
URL manifestUrl = getClass().getClassLoader().getResource(manifestName);
|
||||
if (manifestUrl != null && manifestUrl.getProtocol().equals("file")) {
|
||||
manifest = FileFsFile.from(manifestUrl.getPath());
|
||||
} else if (FileFsFile.from(buildOutputDir, "manifests", "full", flavor, abiSplit, type,
|
||||
manifestName).exists()) {
|
||||
manifest = FileFsFile.from(
|
||||
buildOutputDir, "manifests", "full", flavor, abiSplit, type, manifestName);
|
||||
} else if (FileFsFile.from(buildOutputDir, "manifests", "aapt", flavor, abiSplit, type,
|
||||
manifestName).exists()) {
|
||||
// Android gradle plugin 2.2.0+ can put library manifest files inside of "aapt"
|
||||
// instead of "full"
|
||||
manifest = FileFsFile.from(buildOutputDir, "manifests", "aapt", flavor, abiSplit,
|
||||
type, manifestName);
|
||||
} else {
|
||||
manifest = FileFsFile.from(buildOutputDir, "bundles", flavor, abiSplit, type,
|
||||
manifestName);
|
||||
}
|
||||
|
||||
return new ManifestIdentifier(manifest, res, assets, packageName, null);
|
||||
}
|
||||
|
||||
private static String getBuildOutputDir(Config config) {
|
||||
return config.buildDir() + File.separator + "intermediates";
|
||||
}
|
||||
|
||||
private static String getType(Config config) {
|
||||
try {
|
||||
return ReflectionHelpers.getStaticField(config.constants(), "BUILD_TYPE");
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFlavor(Config config) {
|
||||
try {
|
||||
return ReflectionHelpers.getStaticField(config.constants(), "FLAVOR");
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getAbiSplit(Config config) {
|
||||
try {
|
||||
return config.abiSplit();
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,42 +20,13 @@ import org.junit.runner.notification.RunNotifier;
|
|||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.InitializationError;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.internal.ManifestFactory;
|
||||
|
||||
public class SuwLibRobolectricTestRunner extends RobolectricTestRunner {
|
||||
|
||||
private String mModuleRootPath;
|
||||
|
||||
public SuwLibRobolectricTestRunner(Class<?> testClass) throws InitializationError {
|
||||
super(testClass);
|
||||
}
|
||||
|
||||
// Hack to determine the module root path in the build folder (e.g. out/gradle/setup-wizard-lib)
|
||||
private void updateModuleRootPath(Config config) {
|
||||
String moduleRoot = config.constants().getResource("").toString()
|
||||
.replace("file:", "").replace("jar:", "");
|
||||
mModuleRootPath =
|
||||
moduleRoot.substring(0, moduleRoot.lastIndexOf("/build")) + "/setup-wizard-lib";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default config used to run Robolectric tests.
|
||||
*/
|
||||
@Override
|
||||
protected Config buildGlobalConfig() {
|
||||
Config parent = super.buildGlobalConfig();
|
||||
updateModuleRootPath(parent);
|
||||
return new Config.Builder(parent)
|
||||
.setBuildDir(mModuleRootPath + "/build")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ManifestFactory getManifestFactory(Config config) {
|
||||
return new PatchedGradleManifestFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runChild(FrameworkMethod method, RunNotifier notifier) {
|
||||
System.out.println("===== Running " + method + " =====");
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.setupwizardlib.shadow;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
|
||||
@Implements(Log.class)
|
||||
public class ShadowLog extends org.robolectric.shadows.ShadowLog {
|
||||
|
||||
public static boolean sWtfIsFatal = true;
|
||||
|
||||
public static class TerribleFailure extends RuntimeException {
|
||||
|
||||
public TerribleFailure(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public static void wtf(String tag, String msg) {
|
||||
org.robolectric.shadows.ShadowLog.wtf(tag, msg);
|
||||
if (sWtfIsFatal) {
|
||||
throw new TerribleFailure(msg, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public static void wtf(String tag, String msg, Throwable throwable) {
|
||||
org.robolectric.shadows.ShadowLog.wtf(tag, msg, throwable);
|
||||
if (sWtfIsFatal) {
|
||||
throw new TerribleFailure(msg, throwable);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,26 +16,28 @@
|
|||
|
||||
package com.android.setupwizardlib.span;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.robolectric.RuntimeEnvironment.application;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.text.Selection;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(constants = BuildConfig.class)
|
||||
public class LinkSpanTest {
|
||||
|
||||
@Test
|
||||
public void testOnClick() {
|
||||
public void onClick_shouldCallListenerOnContext() {
|
||||
final TestContext context = new TestContext(application);
|
||||
final TextView textView = new TextView(context);
|
||||
final LinkSpan linkSpan = new LinkSpan("test_id");
|
||||
|
@ -46,7 +48,7 @@ public class LinkSpanTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testNonImplementingContext() {
|
||||
public void onClick_contextDoesNotImplementOnClickListener_shouldBeNoOp() {
|
||||
final TextView textView = new TextView(application);
|
||||
final LinkSpan linkSpan = new LinkSpan("test_id");
|
||||
|
||||
|
@ -57,7 +59,7 @@ public class LinkSpanTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testWrappedListener() {
|
||||
public void onClick_contextWrapsOnClickListener_shouldCallWrappedListener() {
|
||||
final TestContext context = new TestContext(application);
|
||||
final Context wrapperContext = new ContextWrapper(context);
|
||||
final TextView textView = new TextView(wrapperContext);
|
||||
|
@ -68,6 +70,28 @@ public class LinkSpanTest {
|
|||
assertSame("Clicked LinkSpan should be passed to setup", linkSpan, context.clickedSpan);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClick_shouldClearSelection() {
|
||||
final TestContext context = new TestContext(application);
|
||||
final TextView textView = new TextView(context);
|
||||
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
textView.setFocusable(true);
|
||||
textView.setFocusableInTouchMode(true);
|
||||
final LinkSpan linkSpan = new LinkSpan("test_id");
|
||||
|
||||
SpannableStringBuilder text = new SpannableStringBuilder("Lorem ipsum dolor sit");
|
||||
textView.setText(text);
|
||||
text.setSpan(linkSpan, /* start= */ 0, /* end= */ 5, /* flags= */ 0);
|
||||
// Simulate the touch effect set by TextView when touched.
|
||||
Selection.setSelection(text, /* start= */ 0, /* end= */ 5);
|
||||
|
||||
linkSpan.onClick(textView);
|
||||
|
||||
assertThat(Selection.getSelectionStart(textView.getText())).isEqualTo(0);
|
||||
assertThat(Selection.getSelectionEnd(textView.getText())).isEqualTo(0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener {
|
||||
|
||||
public LinkSpan clickedSpan = null;
|
||||
|
|
|
@ -31,7 +31,6 @@ import android.widget.AbsListView.OnScrollListener;
|
|||
import android.widget.BaseAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
|
@ -42,7 +41,7 @@ import org.mockito.Mock;
|
|||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
public class ListViewScrollHandlingDelegateTest {
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ import android.view.View;
|
|||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.TemplateLayout;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
import com.android.setupwizardlib.template.RequireScrollMixin.OnRequireScrollStateChangedListener;
|
||||
|
@ -48,7 +47,7 @@ import org.mockito.Mock;
|
|||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
public class RequireScrollMixinTest {
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ import static org.mockito.Mockito.spy;
|
|||
import static org.mockito.Mockito.verify;
|
||||
import static org.robolectric.RuntimeEnvironment.application;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
import com.android.setupwizardlib.view.BottomScrollView;
|
||||
import com.android.setupwizardlib.view.BottomScrollView.BottomScrollListener;
|
||||
|
@ -36,7 +35,7 @@ import org.mockito.Mock;
|
|||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
public class ScrollViewScrollHandlingDelegateTest {
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ import android.util.DisplayMetrics;
|
|||
import android.util.TypedValue;
|
||||
import android.view.ContextThemeWrapper;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.R;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
||||
|
@ -36,7 +35,7 @@ import org.junit.runner.RunWith;
|
|||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(constants = BuildConfig.class, sdk = Config.ALL_SDKS)
|
||||
@Config(sdk = Config.ALL_SDKS)
|
||||
public class GlifDimensionTest {
|
||||
|
||||
private Context mContext;
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
|
||||
package com.android.setupwizardlib.util;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.robolectric.RuntimeEnvironment.application;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
|
@ -29,8 +31,8 @@ import android.os.Bundle;
|
|||
import android.support.annotation.Nullable;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.R;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
||||
|
@ -41,7 +43,7 @@ import org.robolectric.Robolectric;
|
|||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(constants = BuildConfig.class, sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
|
||||
@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
|
||||
public class GlifStyleTest {
|
||||
|
||||
private Context mContext;
|
||||
|
@ -58,9 +60,8 @@ public class GlifStyleTest {
|
|||
Robolectric.buildAttributeSet()
|
||||
.setStyleAttribute("@style/SuwGlifButton.Tertiary")
|
||||
.build());
|
||||
assertNull("Background of tertiary button should be null", button.getBackground());
|
||||
assertNull("Tertiary button should have no transformation method",
|
||||
button.getTransformationMethod());
|
||||
assertThat(button.getBackground()).named("background").isNotNull();
|
||||
assertThat(button.getTransformationMethod()).named("transformation method").isNull();
|
||||
if (VERSION.SDK_INT < VERSION_CODES.M) {
|
||||
// Robolectric resolved the wrong theme attribute on versions >= M
|
||||
// https://github.com/robolectric/robolectric/issues/2940
|
||||
|
@ -76,6 +77,15 @@ public class GlifStyleTest {
|
|||
assertEquals(0x00000000, activity.getWindow().getStatusBarColor());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void glifLoadingScreen_shouldHaveProgressBar() {
|
||||
GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
|
||||
activity.setContentView(R.layout.suw_glif_loading_screen);
|
||||
|
||||
assertTrue("Progress bar should exist",
|
||||
activity.findViewById(R.id.suw_large_progress_bar) instanceof ProgressBar);
|
||||
}
|
||||
|
||||
private static class GlifThemeActivity extends Activity {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.setupwizardlib.util;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.android.setupwizardlib.R;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(minSdk = Config.OLDEST_SDK, maxSdk = Config.NEWEST_SDK)
|
||||
public class GlifV3StyleTest {
|
||||
|
||||
@Test
|
||||
public void activityWithGlifV3Theme_shouldUseLightNavBarOnV27OrAbove() {
|
||||
GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) {
|
||||
assertEquals(
|
||||
activity.getWindow().getNavigationBarColor(),
|
||||
Color.WHITE);
|
||||
int vis = activity.getWindow().getDecorView().getSystemUiVisibility();
|
||||
assertTrue((vis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0);
|
||||
} else if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
||||
assertEquals(
|
||||
activity.getWindow().getNavigationBarColor(),
|
||||
Color.BLACK);
|
||||
}
|
||||
// Nav bar color is not customizable pre-L
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buttonWithGlifV3_shouldBeGoogleSans() {
|
||||
GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
|
||||
Button button = new Button(
|
||||
activity,
|
||||
Robolectric.buildAttributeSet()
|
||||
.setStyleAttribute("@style/SuwGlifButton.Primary")
|
||||
.build());
|
||||
assertThat(button.getTypeface()).isEqualTo(Typeface.create("google-sans", 0));
|
||||
// Button should not be all caps
|
||||
assertThat(button.getTransformationMethod()).isNull();
|
||||
}
|
||||
|
||||
private static class GlifThemeActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
setTheme(R.style.SuwThemeGlifV3_Light);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,29 +31,33 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.R;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
import com.android.setupwizardlib.util.Partner.ResourceEntry;
|
||||
import com.android.setupwizardlib.util.PartnerTest.ShadowApplicationPackageManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.res.builder.DefaultPackageManager;
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.shadows.ShadowResources;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
@Config(
|
||||
sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK },
|
||||
shadows = ShadowApplicationPackageManager.class)
|
||||
public class PartnerTest {
|
||||
|
||||
private static final String ACTION_PARTNER_CUSTOMIZATION =
|
||||
|
@ -62,7 +66,7 @@ public class PartnerTest {
|
|||
private Context mContext;
|
||||
private Resources mPartnerResources;
|
||||
|
||||
private TestPackageManager mPackageManager;
|
||||
private ShadowApplicationPackageManager mPackageManager;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
@ -71,8 +75,9 @@ public class PartnerTest {
|
|||
mContext = spy(application);
|
||||
mPartnerResources = spy(ShadowResources.getSystem());
|
||||
|
||||
mPackageManager = new TestPackageManager();
|
||||
RuntimeEnvironment.setRobolectricPackageManager(mPackageManager);
|
||||
mPackageManager =
|
||||
(ShadowApplicationPackageManager) Shadows.shadowOf(application.getPackageManager());
|
||||
mPackageManager.partnerResources = mPartnerResources;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -126,6 +131,20 @@ public class PartnerTest {
|
|||
assertTrue("Partner value should come from overlay", entry.isOverlay);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getColor_shouldReturnPartnerValueIfPresent() {
|
||||
final int expectedPartnerColor = 1111;
|
||||
doReturn(12345).when(mPartnerResources)
|
||||
.getIdentifier(eq("suw_color_accent_dark"), eq("color"), anyString());
|
||||
doReturn(expectedPartnerColor).when(mPartnerResources).getColor(eq(12345));
|
||||
mPackageManager.addResolveInfoForIntent(
|
||||
new Intent(ACTION_PARTNER_CUSTOMIZATION),
|
||||
Arrays.asList(createResolveInfo("test.partner.package", true, true)));
|
||||
final int foundColor = Partner.getColor(mContext, R.color.suw_color_accent_dark);
|
||||
assertEquals("Partner color should be overlayed to: " + expectedPartnerColor,
|
||||
expectedPartnerColor, foundColor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadDefaultValue() {
|
||||
mPackageManager.addResolveInfoForIntent(
|
||||
|
@ -173,13 +192,18 @@ public class PartnerTest {
|
|||
return info;
|
||||
}
|
||||
|
||||
private class TestPackageManager extends DefaultPackageManager {
|
||||
@Implements(className = "android.app.ApplicationPackageManager")
|
||||
public static class ShadowApplicationPackageManager extends
|
||||
org.robolectric.shadows.ShadowApplicationPackageManager {
|
||||
|
||||
public Resources partnerResources;
|
||||
|
||||
@Implementation
|
||||
@Override
|
||||
public Resources getResourcesForApplication(ApplicationInfo app)
|
||||
throws NameNotFoundException {
|
||||
if (app != null && "test.partner.package".equals(app.packageName)) {
|
||||
return mPartnerResources;
|
||||
return partnerResources;
|
||||
} else {
|
||||
return super.getResourcesForApplication(app);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ import android.provider.Settings.Global;
|
|||
import android.provider.Settings.Secure;
|
||||
import android.support.annotation.StyleRes;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.R;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
||||
|
@ -39,8 +38,12 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(constants = BuildConfig.class, sdk = Config.NEWEST_SDK)
|
||||
@Config(sdk = Config.NEWEST_SDK)
|
||||
public class WizardManagerHelperTest {
|
||||
|
||||
@Test
|
||||
|
@ -87,6 +90,14 @@ public class WizardManagerHelperTest {
|
|||
WizardManagerHelper.isDeferredSetupWizard(intent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsPreDeferredSetupTrue() {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra("preDeferredSetup", true);
|
||||
assertTrue("Is pre-deferred setup wizard should be true",
|
||||
WizardManagerHelper.isPreDeferredSetupWizard(intent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSetupWizardFalse() {
|
||||
final Intent intent = new Intent();
|
||||
|
@ -96,75 +107,57 @@ public class WizardManagerHelperTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testHoloIsNotLightTheme() {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra("theme", "holo");
|
||||
assertFalse("Theme holo should not be light theme",
|
||||
WizardManagerHelper.isLightTheme(intent, true));
|
||||
public void isLightTheme_shouldReturnTrue_whenThemeIsLight() {
|
||||
List<String> lightThemes = Arrays.asList(
|
||||
"holo_light",
|
||||
"material_light",
|
||||
"glif_light",
|
||||
"glif_v2_light",
|
||||
"glif_v3_light"
|
||||
);
|
||||
ArrayList<String> unexpectedIntentThemes = new ArrayList<>();
|
||||
ArrayList<String> unexpectedStringThemes = new ArrayList<>();
|
||||
for (final String theme : lightThemes) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(WizardManagerHelper.EXTRA_THEME, theme);
|
||||
if (!WizardManagerHelper.isLightTheme(intent, false)) {
|
||||
unexpectedIntentThemes.add(theme);
|
||||
}
|
||||
if (!WizardManagerHelper.isLightTheme(theme, false)) {
|
||||
unexpectedStringThemes.add(theme);
|
||||
}
|
||||
}
|
||||
assertTrue("Intent themes " + unexpectedIntentThemes + " should be light",
|
||||
unexpectedIntentThemes.isEmpty());
|
||||
assertTrue("String themes " + unexpectedStringThemes + " should be light",
|
||||
unexpectedStringThemes.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHoloLightIsLightTheme() {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra("theme", "holo_light");
|
||||
assertTrue("Theme holo_light should be light theme",
|
||||
WizardManagerHelper.isLightTheme(intent, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaterialIsNotLightTheme() {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra("theme", "material");
|
||||
assertFalse("Theme material should not be light theme",
|
||||
WizardManagerHelper.isLightTheme(intent, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaterialLightIsLightTheme() {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra("theme", "material_light");
|
||||
assertTrue("Theme material_light should be light theme",
|
||||
WizardManagerHelper.isLightTheme(intent, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlifIsDarkTheme() {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra("theme", "glif");
|
||||
assertFalse("Theme glif should be dark theme",
|
||||
WizardManagerHelper.isLightTheme(intent, false));
|
||||
assertFalse("Theme glif should be dark theme",
|
||||
WizardManagerHelper.isLightTheme(intent, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlifLightIsLightTheme() {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra("theme", "glif_light");
|
||||
assertTrue("Theme glif_light should be light theme",
|
||||
WizardManagerHelper.isLightTheme(intent, false));
|
||||
assertTrue("Theme glif_light should be light theme",
|
||||
WizardManagerHelper.isLightTheme(intent, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlifV2IsDarkTheme() {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra("theme", "glif_v2");
|
||||
assertFalse("Theme glif_v2 should be dark theme",
|
||||
WizardManagerHelper.isLightTheme(intent, false));
|
||||
assertFalse("Theme glif_v2 should be dark theme",
|
||||
WizardManagerHelper.isLightTheme(intent, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlifV2LightIsLightTheme() {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra("theme", "glif_v2_light");
|
||||
assertTrue("Theme glif_v2_light should be light theme",
|
||||
WizardManagerHelper.isLightTheme(intent, false));
|
||||
assertTrue("Theme glif_v2_light should be light theme",
|
||||
WizardManagerHelper.isLightTheme(intent, true));
|
||||
public void isLightTheme_shouldReturnFalse_whenThemeIsNotLight() {
|
||||
List<String> lightThemes = Arrays.asList(
|
||||
"holo",
|
||||
"material",
|
||||
"glif",
|
||||
"glif_v2",
|
||||
"glif_v3"
|
||||
);
|
||||
ArrayList<String> unexpectedIntentThemes = new ArrayList<>();
|
||||
ArrayList<String> unexpectedStringThemes = new ArrayList<>();
|
||||
for (final String theme : lightThemes) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(WizardManagerHelper.EXTRA_THEME, theme);
|
||||
if (WizardManagerHelper.isLightTheme(intent, true)) {
|
||||
unexpectedIntentThemes.add(theme);
|
||||
}
|
||||
if (WizardManagerHelper.isLightTheme(theme, true)) {
|
||||
unexpectedStringThemes.add(theme);
|
||||
}
|
||||
}
|
||||
assertTrue("Intent themes " + unexpectedIntentThemes + " should not be light",
|
||||
unexpectedIntentThemes.isEmpty());
|
||||
assertTrue("String themes " + unexpectedStringThemes + " should not be light",
|
||||
unexpectedStringThemes.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -187,19 +180,15 @@ public class WizardManagerHelperTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testIsLightThemeString() {
|
||||
assertTrue("isLightTheme should return true for material_light",
|
||||
WizardManagerHelper.isLightTheme("material_light", false));
|
||||
assertFalse("isLightTheme should return false for material",
|
||||
WizardManagerHelper.isLightTheme("material", false));
|
||||
assertTrue("isLightTheme should return true for holo_light",
|
||||
WizardManagerHelper.isLightTheme("holo_light", false));
|
||||
assertFalse("isLightTheme should return false for holo",
|
||||
WizardManagerHelper.isLightTheme("holo", false));
|
||||
assertTrue("isLightTheme should return default value true",
|
||||
WizardManagerHelper.isLightTheme("abracadabra", true));
|
||||
assertFalse("isLightTheme should return default value false",
|
||||
WizardManagerHelper.isLightTheme("abracadabra", false));
|
||||
public void testGetThemeResGlifV3Light() {
|
||||
assertEquals(R.style.SuwThemeGlifV3_Light,
|
||||
WizardManagerHelper.getThemeRes("glif_v3_light", 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetThemeResGlifV3() {
|
||||
assertEquals(R.style.SuwThemeGlifV3,
|
||||
WizardManagerHelper.getThemeRes("glif_v3", 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -266,6 +255,7 @@ public class WizardManagerHelperTest {
|
|||
.putExtra(WizardManagerHelper.EXTRA_WIZARD_BUNDLE, wizardBundle)
|
||||
.putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true)
|
||||
.putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, true)
|
||||
.putExtra(WizardManagerHelper.EXTRA_IS_PRE_DEFERRED_SETUP, true)
|
||||
// Script URI and Action ID are kept for backwards compatibility
|
||||
.putExtra(WizardManagerHelper.EXTRA_SCRIPT_URI, "test_script_uri")
|
||||
.putExtra(WizardManagerHelper.EXTRA_ACTION_ID, "test_action_id");
|
||||
|
@ -284,6 +274,8 @@ public class WizardManagerHelperTest {
|
|||
intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, false));
|
||||
assertTrue("EXTRA_IS_DEFERRED_SETUP should be copied",
|
||||
intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, false));
|
||||
assertTrue("EXTRA_IS_PRE_DEFERRED_SETUP should be copied",
|
||||
intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_PRE_DEFERRED_SETUP, false));
|
||||
|
||||
// Script URI and Action ID are replaced by Wizard Bundle in M, but are kept for backwards
|
||||
// compatibility
|
||||
|
|
|
@ -22,7 +22,6 @@ import static org.robolectric.RuntimeEnvironment.application;
|
|||
import android.view.View;
|
||||
import android.view.View.MeasureSpec;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
|
||||
import org.junit.Test;
|
||||
|
@ -31,7 +30,7 @@ import org.robolectric.Robolectric;
|
|||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(constants = BuildConfig.class, sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
|
||||
@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
|
||||
public class FillContentLayoutTest {
|
||||
|
||||
@Test
|
||||
|
|
|
@ -18,6 +18,7 @@ package com.android.setupwizardlib.view;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
@ -31,9 +32,10 @@ import android.os.Build.VERSION_CODES;
|
|||
import android.support.annotation.RawRes;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.setupwizardlib.BuildConfig;
|
||||
import com.android.setupwizardlib.R;
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
import com.android.setupwizardlib.shadow.ShadowLog;
|
||||
import com.android.setupwizardlib.shadow.ShadowLog.TerribleFailure;
|
||||
import com.android.setupwizardlib.view.IllustrationVideoViewTest.ShadowMockMediaPlayer;
|
||||
import com.android.setupwizardlib.view.IllustrationVideoViewTest.ShadowSurface;
|
||||
|
||||
|
@ -48,15 +50,15 @@ import org.robolectric.annotation.Config;
|
|||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.annotation.RealObject;
|
||||
import org.robolectric.internal.Shadow;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
import org.robolectric.shadows.ShadowMediaPlayer;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(
|
||||
constants = BuildConfig.class,
|
||||
sdk = Config.NEWEST_SDK,
|
||||
shadows = {
|
||||
ShadowLog.class,
|
||||
ShadowMockMediaPlayer.class,
|
||||
ShadowSurface.class
|
||||
})
|
||||
|
@ -77,6 +79,17 @@ public class IllustrationVideoViewTest {
|
|||
ShadowMockMediaPlayer.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullMediaPlayer_shouldThrowWtf() {
|
||||
ShadowMockMediaPlayer.sMediaPlayer = null;
|
||||
try {
|
||||
createDefaultView();
|
||||
fail("WTF should be thrown for null media player");
|
||||
} catch (TerribleFailure e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPausedWhenWindowFocusLost() {
|
||||
createDefaultView();
|
||||
|
|
|
@ -14,39 +14,45 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.setupwizardlib.test;
|
||||
package com.android.setupwizardlib.view;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.robolectric.RuntimeEnvironment.application;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.text.Annotation;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.TextAppearanceSpan;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
|
||||
import com.android.setupwizardlib.span.LinkSpan;
|
||||
import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener;
|
||||
import com.android.setupwizardlib.view.RichTextView;
|
||||
import com.android.setupwizardlib.view.TouchableMovementMethod.TouchableLinkMovementMethod;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
@RunWith(SuwLibRobolectricTestRunner.class)
|
||||
@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
|
||||
public class RichTextViewTest {
|
||||
|
||||
@Test
|
||||
|
@ -55,12 +61,14 @@ public class RichTextViewTest {
|
|||
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
|
||||
ssb.setSpan(link, 1, 2, 0 /* flags */);
|
||||
|
||||
RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
|
||||
RichTextView textView = new RichTextView(application);
|
||||
textView.setText(ssb);
|
||||
|
||||
final CharSequence text = textView.getText();
|
||||
assertTrue("Text should be spanned", text instanceof Spanned);
|
||||
|
||||
assertThat(textView.getMovementMethod()).isInstanceOf(TouchableLinkMovementMethod.class);
|
||||
|
||||
Object[] spans = ((Spanned) text).getSpans(0, text.length(), Annotation.class);
|
||||
assertEquals("Annotation should be removed " + Arrays.toString(spans), 0, spans.length);
|
||||
|
||||
|
@ -77,7 +85,7 @@ public class RichTextViewTest {
|
|||
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
|
||||
ssb.setSpan(link, 1, 2, 0 /* flags */);
|
||||
|
||||
RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
|
||||
RichTextView textView = new RichTextView(application);
|
||||
textView.setText(ssb);
|
||||
|
||||
OnLinkClickListener listener = mock(OnLinkClickListener.class);
|
||||
|
@ -99,7 +107,7 @@ public class RichTextViewTest {
|
|||
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
|
||||
ssb.setSpan(link, 1, 2, 0 /* flags */);
|
||||
|
||||
TestContext context = spy(new TestContext(InstrumentationRegistry.getTargetContext()));
|
||||
TestContext context = spy(new TestContext(application));
|
||||
RichTextView textView = new RichTextView(context);
|
||||
textView.setText(ssb);
|
||||
|
||||
|
@ -110,13 +118,51 @@ public class RichTextViewTest {
|
|||
verify(context).onClick(eq(spans[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTouchEvent_clickOnLinks_shouldReturnTrue() {
|
||||
Annotation link = new Annotation("link", "foobar");
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
|
||||
ssb.setSpan(link, 0, 2, 0 /* flags */);
|
||||
|
||||
RichTextView textView = new RichTextView(application);
|
||||
textView.setText(ssb);
|
||||
|
||||
TouchableLinkMovementMethod mockMovementMethod = mock(TouchableLinkMovementMethod.class);
|
||||
textView.setMovementMethod(mockMovementMethod);
|
||||
|
||||
MotionEvent motionEvent =
|
||||
MotionEvent.obtain(123, 22, MotionEvent.ACTION_DOWN, 0, 0, 0);
|
||||
doReturn(motionEvent).when(mockMovementMethod).getLastTouchEvent();
|
||||
doReturn(true).when(mockMovementMethod).isLastTouchEventHandled();
|
||||
assertThat(textView.onTouchEvent(motionEvent)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTouchEvent_clickOutsideLinks_shouldReturnFalse() {
|
||||
Annotation link = new Annotation("link", "foobar");
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
|
||||
ssb.setSpan(link, 0, 2, 0 /* flags */);
|
||||
|
||||
RichTextView textView = new RichTextView(application);
|
||||
textView.setText(ssb);
|
||||
|
||||
TouchableLinkMovementMethod mockMovementMethod = mock(TouchableLinkMovementMethod.class);
|
||||
textView.setMovementMethod(mockMovementMethod);
|
||||
|
||||
MotionEvent motionEvent =
|
||||
MotionEvent.obtain(123, 22, MotionEvent.ACTION_DOWN, 0, 0, 0);
|
||||
doReturn(motionEvent).when(mockMovementMethod).getLastTouchEvent();
|
||||
doReturn(false).when(mockMovementMethod).isLastTouchEventHandled();
|
||||
assertThat(textView.onTouchEvent(motionEvent)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTextStyle() {
|
||||
Annotation link = new Annotation("textAppearance", "foobar");
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
|
||||
ssb.setSpan(link, 1, 2, 0 /* flags */);
|
||||
|
||||
RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
|
||||
RichTextView textView = new RichTextView(application);
|
||||
textView.setText(ssb);
|
||||
|
||||
final CharSequence text = textView.getText();
|
||||
|
@ -137,7 +183,7 @@ public class RichTextViewTest {
|
|||
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("Linked");
|
||||
spannableStringBuilder.setSpan(testLink, 0, 3, 0);
|
||||
|
||||
RichTextView view = new RichTextView(InstrumentationRegistry.getContext());
|
||||
RichTextView view = new RichTextView(application);
|
||||
view.setText(spannableStringBuilder);
|
||||
|
||||
assertTrue("TextView should be focusable since it contains spans", view.isFocusable());
|
||||
|
@ -147,7 +193,7 @@ public class RichTextViewTest {
|
|||
@SuppressLint("SetTextI18n") // It's OK. This is just a test.
|
||||
@Test
|
||||
public void testTextContainingNoLinksAreNotFocusable() {
|
||||
RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
|
||||
RichTextView textView = new RichTextView(application);
|
||||
textView.setText("Thou shall not be focusable!");
|
||||
|
||||
assertFalse("TextView should not be focusable since it does not contain any span",
|
||||
|
@ -160,16 +206,23 @@ public class RichTextViewTest {
|
|||
@SuppressLint("SetTextI18n") // It's OK. This is just a test.
|
||||
@Test
|
||||
public void testRichTextViewFocusChangesWithTextChange() {
|
||||
RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
|
||||
RichTextView textView = new RichTextView(application);
|
||||
textView.setText("Thou shall not be focusable!");
|
||||
|
||||
assertFalse(textView.isFocusable());
|
||||
assertFalse(textView.isFocusableInTouchMode());
|
||||
|
||||
SpannableStringBuilder spannableStringBuilder =
|
||||
new SpannableStringBuilder("I am focusable");
|
||||
spannableStringBuilder.setSpan(new Annotation("link", "focus:on_me"), 0, 1, 0);
|
||||
textView.setText(spannableStringBuilder);
|
||||
assertTrue(textView.isFocusable());
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
|
||||
assertTrue(textView.isFocusableInTouchMode());
|
||||
assertFalse(textView.getRevealOnFocusHint());
|
||||
} else {
|
||||
assertFalse(textView.isFocusableInTouchMode());
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener {
|
6
navigationbar/res/values-en-rCA/strings.xml
Normal file
6
navigationbar/res/values-en-rCA/strings.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="setup_wizard_next_button_label" msgid="6681282266022780599">"Next"</string>
|
||||
<string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Back"</string>
|
||||
</resources>
|
6
navigationbar/res/values-en-rXC/strings.xml
Normal file
6
navigationbar/res/values-en-rXC/strings.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="setup_wizard_next_button_label" msgid="6681282266022780599">"Next"</string>
|
||||
<string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Back"</string>
|
||||
</resources>
|
|
@ -6,4 +6,4 @@ export TARGET_BUILD_DENSITY="alldpi"
|
|||
export TARGET_BUILD_TYPE="release"
|
||||
export TARGET_BUILD_APPS="setup-wizard-lib"
|
||||
|
||||
./gradlew buildProjectFull test
|
||||
./gradlew buildProjectFull test coverage
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Set the default SDK and build tools version for all apps
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion = '25.0.0'
|
||||
compileSdkVersion 28
|
||||
buildToolsVersion = '28.0.0'
|
||||
|
||||
// enable Java7
|
||||
compileOptions.sourceCompatibility JavaVersion.VERSION_1_7
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
apply plugin: 'dist'
|
||||
apply plugin: 'jacoco'
|
||||
|
||||
// If unit tests are run as part of the build, dist the test XML reports to host-test-reports/*.zip
|
||||
android.unitTestVariants.all { variant ->
|
||||
|
@ -28,11 +29,80 @@ android.unitTestVariants.all { variant ->
|
|||
archiveName = task.name + 'Result.zip'
|
||||
destinationDir = junitReport.destination.parentFile
|
||||
}
|
||||
zipTask.mustRunAfter task
|
||||
task.finalizedBy zipTask
|
||||
|
||||
// Copy the test reports to dist/host-test-reports
|
||||
// The file path and format should match GradleHostBasedTest class in TradeFed.
|
||||
tasks.dist.dependsOn zipTask
|
||||
tasks.dist.mustRunAfter zipTask
|
||||
dist.file zipTask.archivePath.path, "host-test-reports/${zipTask.archiveName}"
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The section below adds code coverage to all the unitTest targets. By default, the jacoco plugin
|
||||
* only adds to the 'java' plugin and not the Android ones.
|
||||
*
|
||||
* For each unitTest task "fooUnitTest", a new target "fooUnitTestCoverage" will be generated for
|
||||
* to generate the jacoco report.
|
||||
*/
|
||||
android.testOptions.unitTests.all {
|
||||
// Fix robolectric tests reporting 0 coverage on newer versions of the plugin.
|
||||
jacoco {
|
||||
includeNoLocationClasses = true
|
||||
}
|
||||
}
|
||||
|
||||
// Define the main coverage task if it does not exist. This task generates coverage report for all
|
||||
// unit tests.
|
||||
def coverageTask = tasks.findByName('coverage') ?: tasks.create('coverage') {
|
||||
group = "Reporting"
|
||||
description = "Generate Jacoco coverage reports"
|
||||
}
|
||||
|
||||
android.unitTestVariants.all { variant ->
|
||||
def testTaskName = "test${variant.name.capitalize()}"
|
||||
def testTask = tasks.findByName(testTaskName)
|
||||
|
||||
// Create coverage task of form 'testFlavorCoverageUnitTestCoverage' depending on
|
||||
// 'testFlavorCoverageUnitTest'
|
||||
def jacocoTask = tasks.create("${testTaskName}Coverage", JacocoReport) {
|
||||
group = "Reporting"
|
||||
description = "Generate a Jacoco coverage report for robolectric tests on ${variant.name}."
|
||||
|
||||
classDirectories = fileTree(
|
||||
dir: "${project.buildDir}/intermediates/classes/" +
|
||||
"${variant.productFlavors[0].name}/${variant.buildType.name}",
|
||||
excludes: ['**/R.class',
|
||||
'**/R$*.class',
|
||||
'**/BuildConfig.*',
|
||||
'**/Manifest*.*']
|
||||
)
|
||||
|
||||
sourceDirectories = files(variant.testedVariant.sourceSets.collect { it.java.srcDirs })
|
||||
executionData = files("${project.buildDir}/jacoco/${testTaskName}.exec")
|
||||
|
||||
reports {
|
||||
xml.enabled = true
|
||||
html.enabled = true
|
||||
}
|
||||
}
|
||||
jacocoTask.dependsOn testTask
|
||||
|
||||
// Create a zip file of the HTML coverage reports
|
||||
def zipTask = tasks.create("zipResultsOf${jacocoTask.name.capitalize()}", Zip) {
|
||||
from jacocoTask.reports.html.destination
|
||||
archiveName = "${testTaskName}HtmlCoverage.zip"
|
||||
destinationDir = jacocoTask.reports.html.destination.parentFile
|
||||
}
|
||||
jacocoTask.finalizedBy zipTask
|
||||
|
||||
// Copy the coverage reports to dist/host-test-coverage
|
||||
// The file path and format should match JacocoLogForwarder class in TradeFed.
|
||||
tasks.dist.mustRunAfter jacocoTask
|
||||
dist.file jacocoTask.reports.xml.destination.path, "host-test-coverage/${jacocoTask.name}.xml"
|
||||
|
||||
tasks.dist.mustRunAfter zipTask
|
||||
dist.file zipTask.archivePath.path, "host-test-coverage/${zipTask.archiveName}"
|
||||
|
||||
coverageTask.dependsOn(jacocoTask)
|
||||
}
|
||||
|
|
2
tools/root.mk
Normal file
2
tools/root.mk
Normal file
|
@ -0,0 +1,2 @@
|
|||
default:
|
||||
TARGET_BUILD_APPS=setup-wizard-lib ./gradlew
|
Loading…
Reference in a new issue