Skip to content

Commit

Permalink
Update ShadowDatePickerDialog to use reflector
Browse files Browse the repository at this point in the history
Make getYear(), getMonthOfYear(), getDayOfMonth() and
getOnDateSetListenerCallback() use the "real" values instead of saving
everything in the shadow.

PiperOrigin-RevId: 408807209
  • Loading branch information
Googler authored and copybara-robolectric committed Nov 19, 2021
1 parent 0cb21aa commit bac7890
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 33 deletions.
@@ -1,6 +1,8 @@
package org.robolectric.shadows;

import static android.os.Build.VERSION_CODES.N;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.robolectric.Shadows.shadowOf;

import android.app.DatePickerDialog;
Expand All @@ -9,12 +11,13 @@
import java.util.Locale;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;

@RunWith(AndroidJUnit4.class)
public class ShadowDatePickerDialogTest {

@Test
public void returnsTheInitialYearMonthAndDayPassedIntoTheDatePickerDialog() {
public void testGettersReturnInitialConstructorValues() {
Locale.setDefault(Locale.US);
DatePickerDialog datePickerDialog =
new DatePickerDialog(ApplicationProvider.getApplicationContext(), null, 2012, 6, 7);
Expand All @@ -23,6 +26,33 @@ public void returnsTheInitialYearMonthAndDayPassedIntoTheDatePickerDialog() {
assertThat(shadowOf(datePickerDialog).getDayOfMonth()).isEqualTo(7);
}

@Test
public void updateDate_shouldUpdateYearMonthAndDay() {
Locale.setDefault(Locale.US);
DatePickerDialog datePickerDialog =
new DatePickerDialog(ApplicationProvider.getApplicationContext(), null, 2012, 6, 7);
datePickerDialog.updateDate(2021, 11, 10);

assertThat(shadowOf(datePickerDialog).getYear()).isEqualTo(2021);
assertThat(shadowOf(datePickerDialog).getMonthOfYear()).isEqualTo(11);
assertThat(shadowOf(datePickerDialog).getDayOfMonth()).isEqualTo(10);
}

@Test
@Config(minSdk = N)
public void updateListener_shouldUpdateTheListenerPassedInto() {
DatePickerDialog.OnDateSetListener mockCallBack =
mock(DatePickerDialog.OnDateSetListener.class);
DatePickerDialog datePickerDialog =
new DatePickerDialog(ApplicationProvider.getApplicationContext(), null, 2012, 6, 7);
assertThat(shadowOf(datePickerDialog).getOnDateSetListenerCallback()).isNull();

// setOnDateSetListener added in Android Nougat
datePickerDialog.setOnDateSetListener(mockCallBack);

assertThat(shadowOf(datePickerDialog).getOnDateSetListenerCallback()).isEqualTo(mockCallBack);
}

@Test
public void savesTheCallback() {
DatePickerDialog.OnDateSetListener expectedDateSetListener =
Expand All @@ -37,4 +67,5 @@ public void savesTheCallback() {
ShadowDatePickerDialog shadowDatePickerDialog = shadowOf(datePickerDialog);
assertThat(shadowDatePickerDialog.getOnDateSetListenerCallback()).isEqualTo(expectedDateSetListener);
}

}
Expand Up @@ -8,6 +8,7 @@
import static android.os.Build.VERSION_CODES.N;
import static android.os.Build.VERSION_CODES.Q;
import static org.robolectric.RuntimeEnvironment.getApiLevel;
import static org.robolectric.util.reflector.Reflector.reflector;

import android.graphics.Rect;
import android.os.Bundle;
Expand Down Expand Up @@ -36,7 +37,9 @@
import org.robolectric.annotation.Resetter;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.ForType;
import org.robolectric.util.reflector.Static;

/**
* Properties of {@link android.view.accessibility.AccessibilityNodeInfo} that are normally locked
Expand Down Expand Up @@ -191,8 +194,7 @@ public AccessibilityNodeInfo[] newArray(int size) {

@Implementation
protected void __constructor__() {
ReflectionHelpers.setStaticField(
AccessibilityNodeInfo.class, "CREATOR", ShadowAccessibilityNodeInfo.CREATOR);
reflector(AccessibilityNodeInfoReflector.class).setCreator(ShadowAccessibilityNodeInfo.CREATOR);
}

@Implementation
Expand Down Expand Up @@ -1223,11 +1225,7 @@ public static final class ShadowAccessibilityAction {

@Implementation
protected void __constructor__(int id, CharSequence label) {
if (((id
& (int)
ReflectionHelpers.getStaticField(
AccessibilityNodeInfo.class, "ACTION_TYPE_MASK"))
== 0)
if (((id & reflector(AccessibilityNodeInfoReflector.class).getActionTypeMask()) == 0)
&& Integer.bitCount(id) != 1) {
throw new IllegalArgumentException("Invalid standard action id");
}
Expand Down Expand Up @@ -1266,8 +1264,8 @@ public boolean equals(Object other) {

@Override
public String toString() {
String actionSybolicName = ReflectionHelpers.callStaticMethod(
AccessibilityNodeInfo.class, "getActionSymbolicName", ClassParameter.from(int.class, id));
String actionSybolicName =
reflector(AccessibilityNodeInfoReflector.class).getActionSymbolicName(id);
return "AccessibilityAction: " + actionSybolicName + " - " + label;
}
}
Expand All @@ -1292,19 +1290,16 @@ protected void writeToParcel(Parcel dest, int flags) {

private static int getActionTypeMaskFromFramework() {
// Get the mask to determine whether an int is a legit ID for an action, defined by Android
return (int)ReflectionHelpers.getStaticField(AccessibilityNodeInfo.class, "ACTION_TYPE_MASK");
return reflector(AccessibilityNodeInfoReflector.class).getActionTypeMask();
}

private static AccessibilityAction getActionFromIdFromFrameWork(int id) {
// Convert an action ID to Android standard Accessibility Action defined by Android
return ReflectionHelpers.callStaticMethod(
AccessibilityNodeInfo.class, "getActionSingleton", ClassParameter.from(int.class, id));
return reflector(AccessibilityNodeInfoReflector.class).getActionSingleton(id);
}

private static int getLastLegacyActionFromFrameWork() {
return (int)
ReflectionHelpers.getStaticField(
AccessibilityNodeInfo.class, "LAST_LEGACY_STANDARD_ACTION");
return reflector(AccessibilityNodeInfoReflector.class).getLastLegacyStandardAction();
}

/**
Expand All @@ -1331,4 +1326,25 @@ public String toString() {
+ className
+ "}";
}

@ForType(AccessibilityNodeInfo.class)
interface AccessibilityNodeInfoReflector {
@Static
@Accessor("CREATOR")
void setCreator(Parcelable.Creator<AccessibilityNodeInfo> creator);

@Static
@Accessor("ACTION_TYPE_MASK")
int getActionTypeMask();

@Static
@Accessor("LAST_LEGACY_STANDARD_ACTION")
int getLastLegacyStandardAction();

@Static
String getActionSymbolicName(int id);

@Static
AccessibilityAction getActionSingleton(int id);
}
}
Expand Up @@ -73,6 +73,7 @@
import org.robolectric.res.android.ResXMLTree;
import org.robolectric.res.android.ResourceTypes.Res_value;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.PerfStatsCollector;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import org.robolectric.util.reflector.Direct;
Expand Down Expand Up @@ -1388,6 +1389,30 @@ protected static void nativeApplyStyle(
@NonNull int[] java_attrs,
long out_values_ptr,
long out_indices_ptr) {
PerfStatsCollector.getInstance()
.measure(
"applyStyle",
() ->
nativeApplyStyle_measured(
ptr,
theme_ptr,
def_style_attr,
def_style_resid,
xml_parser_ptr,
java_attrs,
out_values_ptr,
out_indices_ptr));
}

private static void nativeApplyStyle_measured(
long ptr,
long theme_ptr,
@AttrRes int def_style_attr,
@StyleRes int def_style_resid,
long xml_parser_ptr,
@NonNull int[] java_attrs,
long out_values_ptr,
long out_indices_ptr) {
CppAssetManager2 assetmanager = AssetManagerFromLong(ptr);
Theme theme = Registries.NATIVE_THEME9_REGISTRY.getNativeObject(theme_ptr);
CHECK(theme.GetAssetManager() == assetmanager);
Expand Down
Expand Up @@ -73,6 +73,7 @@
import org.robolectric.res.android.ResXMLTree;
import org.robolectric.res.android.ResourceTypes.Res_value;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.PerfStatsCollector;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import org.robolectric.util.reflector.Direct;
Expand Down Expand Up @@ -1378,6 +1379,30 @@ protected static void nativeApplyStyle(
@NonNull int[] java_attrs,
long out_values_ptr,
long out_indices_ptr) {
PerfStatsCollector.getInstance()
.measure(
"applyStyle",
() ->
nativeApplyStyle_measured(
ptr,
theme_ptr,
def_style_attr,
def_style_resid,
xml_parser_ptr,
java_attrs,
out_values_ptr,
out_indices_ptr));
}

private static void nativeApplyStyle_measured(
long ptr,
long theme_ptr,
@AttrRes int def_style_attr,
@StyleRes int def_style_resid,
long xml_parser_ptr,
@NonNull int[] java_attrs,
long out_values_ptr,
long out_indices_ptr) {
CppAssetManager2 assetmanager = AssetManagerFromLong(ptr);
Theme theme = Registries.NATIVE_THEME9_REGISTRY.getNativeObject(theme_ptr);
CHECK(theme.GetAssetManager() == assetmanager);
Expand Down
@@ -1,26 +1,31 @@
package org.robolectric.shadows;

import static android.os.Build.VERSION_CODES.KITKAT;
import static android.os.Build.VERSION_CODES.KITKAT_WATCH;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.N;
import static org.robolectric.shadow.api.Shadow.invokeConstructor;
import static org.robolectric.util.ReflectionHelpers.ClassParameter;
import static org.robolectric.util.reflector.Reflector.reflector;

import android.annotation.TargetApi;
import android.app.DatePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.content.Context;
import androidx.annotation.RequiresApi;
import java.util.Calendar;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.ForType;

@Implements(DatePickerDialog.class)
public class ShadowDatePickerDialog extends ShadowAlertDialog {

@RealObject protected DatePickerDialog realDatePickerDialog;
private Calendar calendar;
private int year;
private int monthOfYear;
private int dayOfMonth;
private DatePickerDialog.OnDateSetListener callBack;

@Implementation(maxSdk = M)
protected void __constructor__(
Expand All @@ -30,10 +35,6 @@ protected void __constructor__(
int year,
int monthOfYear,
int dayOfMonth) {
this.year = year;
this.monthOfYear = monthOfYear;
this.dayOfMonth = dayOfMonth;
this.callBack = callBack;

invokeConstructor(DatePickerDialog.class, realDatePickerDialog,
ClassParameter.from(Context.class, context),
Expand All @@ -54,10 +55,6 @@ protected void __constructor__(
int monthOfYear,
int dayOfMonth) {
this.calendar = calendar;
this.year = year;
this.monthOfYear = monthOfYear;
this.dayOfMonth = dayOfMonth;
this.callBack = callBack;

invokeConstructor(DatePickerDialog.class, realDatePickerDialog,
ClassParameter.from(Context.class, context),
Expand All @@ -74,18 +71,36 @@ public Calendar getCalendar() {
}

public int getYear() {
return year;
return realDatePickerDialog.getDatePicker().getYear();
}

public int getMonthOfYear() {
return monthOfYear;
return realDatePickerDialog.getDatePicker().getMonth();
}

public int getDayOfMonth() {
return dayOfMonth;
return realDatePickerDialog.getDatePicker().getDayOfMonth();
}

public DatePickerDialog.OnDateSetListener getOnDateSetListenerCallback() {
return this.callBack;
if (RuntimeEnvironment.getApiLevel() <= KITKAT) {
return reflector(DatePickerDialogReflector.class, realDatePickerDialog).getCallback();
} else {
return reflector(DatePickerDialogReflector.class, realDatePickerDialog).getDateSetListener();
}
}

@ForType(DatePickerDialog.class)
interface DatePickerDialogReflector {

/** For sdk version at least {@link KITKAT_WATCH} */
@RequiresApi(KITKAT_WATCH)
@Accessor("mDateSetListener")
OnDateSetListener getDateSetListener();

/** For sdk version is equals to {@link KITKAT} */
@TargetApi(KITKAT)
@Accessor("mCallBack")
OnDateSetListener getCallback();
}
}

0 comments on commit bac7890

Please sign in to comment.