Snap for 5143127 from 4b19fecd50 to qt-release

Change-Id: Id545a8cbf60928830aab8549139703080e48bd2a
This commit is contained in:
android-build-team Robot
2018-11-21 04:11:45 +00:00
41 changed files with 539 additions and 279 deletions

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/ContextualCardStyle">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/homepage_condition_footer_height"
android:contentDescription="@string/homepage_condition_footer_content_description"
android:gravity="end"
android:orientation="horizontal"
android:paddingTop="@dimen/homepage_condition_footer_padding_top"
android:paddingEnd="@dimen/homepage_condition_footer_padding_end">
<ImageView
android:id="@+id/collapse_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_expand_less" />
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/ContextualCardStyle">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/homepage_condition_header_padding_top"
android:paddingBottom="@dimen/homepage_condition_header_padding_bottom"
android:orientation="horizontal"
android:gravity="center_vertical">
<LinearLayout
android:id="@+id/header_icons_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginStart="@dimen/homepage_condition_header_icons_margin_start"
android:orientation="horizontal"
android:gravity="center_vertical"/>
<ImageView
android:id="@+id/expand_indicator"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingTop="@dimen/homepage_condition_header_indicator_padding_top"
android:paddingStart="@dimen/homepage_condition_header_indicator_padding_start"
android:paddingEnd="@dimen/homepage_condition_header_indicator_padding_end"
android:src="@drawable/ic_expand_more"/>
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/icon"
android:layout_width="@dimen/homepage_condition_header_icon_width_height"
android:layout_height="@dimen/homepage_condition_header_icon_width_height"
android:layout_marginEnd="@dimen/homepage_condition_header_icon_margin_end"
android:tint="?android:attr/colorAccent"/>

View File

@@ -20,44 +20,48 @@
android:id="@+id/instant_app_button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="24dp"
android:paddingStart="68dp"
android:paddingEnd="24dp"
android:orientation="horizontal">
android:paddingStart="8dp"
android:paddingEnd="8dp">
<FrameLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content">
<Button
android:id="@+id/install"
style="@style/ActionPrimaryButton"
style="@style/SettingsActionButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="@string/install_text"/>
android:paddingTop="20dp"
android:paddingBottom="20dp"
android:text="@string/install_text"
android:drawableTop="@drawable/ic_settings_install"/>
<Button
android:id="@+id/launch"
style="@style/ActionPrimaryButton"
style="@style/SettingsActionButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="@string/launch_instant_app"/>
android:paddingTop="20dp"
android:paddingBottom="20dp"
android:text="@string/launch_instant_app"
android:drawableTop="@drawable/ic_settings_open"/>
</FrameLayout>
<Space
android:layout_width="16dp"
android:layout_width="8dp"
android:layout_height="wrap_content" />
<FrameLayout
<Button
android:id="@+id/clear_data"
style="@style/SettingsActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content">
<Button
android:id="@+id/clear_data"
style="@style/ActionSecondaryButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="@string/clear_instant_app_data"/>
</FrameLayout>
android:paddingTop="20dp"
android:paddingBottom="20dp"
android:text="@string/clear_instant_app_data"
android:drawableTop="@drawable/ic_settings_delete"/>
</LinearLayout>

View File

@@ -198,6 +198,12 @@
+ (fingerprint_enrolling_content_margin_vertical x 2) -->
<dimen name="fingerprint_finish_max_size">288dp</dimen>
<!-- Face -->
<dimen name="face_preview_translate_y">0dp</dimen>
<dimen name="face_preview_translate_x">0dp</dimen>
<item name="face_preview_scale" format="float" type="dimen">1.0</item>
<!-- Confirm device credentials -->
<dimen name="confirm_credentials_security_method_margin">48dp</dimen>
<dimen name="confirm_credentials_layout_width">@dimen/match_parent</dimen>
<dimen name="confirm_credentials_top_padding">0dp</dimen>
@@ -349,5 +355,16 @@
<dimen name="homepage_condition_full_card_padding_end">24dp</dimen>
<dimen name="homepage_condition_full_card_padding_top">12dp</dimen>
<dimen name="homepage_condition_full_card_padding_bottom">12dp</dimen>
<dimen name="homepage_condition_header_padding_top">10dp</dimen>
<dimen name="homepage_condition_header_padding_bottom">10dp</dimen>
<dimen name="homepage_condition_header_icons_margin_start">24dp</dimen>
<dimen name="homepage_condition_header_indicator_padding_top">4dp</dimen>
<dimen name="homepage_condition_header_indicator_padding_start">16dp</dimen>
<dimen name="homepage_condition_header_indicator_padding_end">16dp</dimen>
<dimen name="homepage_condition_footer_height">44dp</dimen>
<dimen name="homepage_condition_footer_padding_top">10dp</dimen>
<dimen name="homepage_condition_footer_padding_end">10dp</dimen>
<dimen name="homepage_condition_header_icon_width_height">24dp</dimen>
<dimen name="homepage_condition_header_icon_margin_end">24dp</dimen>
</resources>

View File

@@ -9105,6 +9105,9 @@
<!-- Summary for the condition section on the dashboard, representing number of conditions. [CHAR LIMIT=10] -->
<string name="condition_summary" translatable="false"><xliff:g name="count" example="3">%1$d</xliff:g></string>
<!-- Content description for condition footer button. In talkback mode, double tapping will cause condition list to collapse [CHAR LIMIT=NONE]-->
<string name="homepage_condition_footer_content_description">Collapse</string>
<!-- Title for the suggestions section on the dashboard [CHAR LIMIT=30] -->
<string name="suggestions_title_v2">Suggested for You</string>
@@ -10315,10 +10318,10 @@
<!-- UI debug setting: force desktop mode summary [CHAR LIMIT=NONE] -->
<string name="force_desktop_mode_summary">Force experimental desktop mode on secondary displays</string>
<!-- UI debug setting: Force enable "smart dark" UI rendering feature [CHAR LIMIT=25] -->
<string name="hwui_force_dark_title">Force-enable SmartDark</string>
<!-- UI debug setting: Force enable "smart dark" UI rendering feature summary [CHAR LIMIT=50] -->
<string name="hwui_force_dark_summary">Forces the SmartDark feature to be always-on</string>
<!-- UI debug setting: Force enable "smart dark" UI rendering feature [CHAR LIMIT=40] -->
<string name="hwui_force_dark_title">Override force-dark</string>
<!-- UI debug setting: Force enable "smart dark" UI rendering feature summary [CHAR LIMIT=100] -->
<string name="hwui_force_dark_summary">Overrides the force-dark feature to be always-on</string>
<!-- Title for the top level Privacy Settings [CHAR LIMIT=30]-->
<string name="privacy_dashboard_title">Privacy</string>

View File

@@ -264,8 +264,8 @@
<style name="TextAppearance.Medium" parent="@android:style/TextAppearance.Material.Medium"/>
<style name="TextAppearance.Small" parent="@android:style/TextAppearance.Material.Small"/>
<style name="TextAppearance.Switch" parent="@android:style/TextAppearance.Material.Title">
<item name="android:textSize">18sp</item>
<style name="TextAppearance.Switch" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
<item name="android:textSize">16sp</item>
</style>
<style name="TextAppearance.CategoryTitle"

View File

@@ -32,7 +32,9 @@
android:key="instant_app_buttons"
android:layout="@layout/instant_app_buttons"
android:selectable="false"
android:order="-9999" />
android:order="-9999"
settings:allowDividerAbove="true"
settings:allowDividerBelow="true"/>
<com.android.settings.widget.ActionButtonPreference
android:key="action_buttons"

View File

@@ -24,7 +24,8 @@
<com.android.settings.applications.SpacePreference
android:key="storage_space"
android:layout_height="8dp" />
android:layout_height="8dp"
settings:allowDividerAbove="true"/>
<Preference
android:key="storage_used"
@@ -41,7 +42,6 @@
<PreferenceCategory
android:key="storage_category"
android:title="@string/app_info_storage_title"
settings:allowDividerAbove="false"
settings:allowDividerBelow="false">
<Preference

View File

@@ -23,7 +23,8 @@
android:key="header_view"
android:layout="@layout/settings_entity_header"
android:selectable="false"
android:order="-10000"/>
android:order="-10000"
settings:allowDividerBelow="true"/>
<com.android.settings.widget.ActionButtonPreference
android:key="action_buttons"

View File

@@ -55,27 +55,37 @@ public class AvatarViewMixin implements LifecycleObserver {
private static final String METHOD_GET_ACCOUNT_AVATAR = "getAccountAvatar";
private static final String KEY_AVATAR_BITMAP = "account_avatar";
private static final String KEY_ACCOUNT_NAME = "account_name";
private static final String EXTRA_ACCOUNT_NAME = "extra.accountName";
private static final int REQUEST_CODE = 1013;
private final Context mContext;
private final ImageView mAvatarView;
private final MutableLiveData<Bitmap> mAvatarImage;
private String mAccountName;
public AvatarViewMixin(SettingsHomepageActivity activity, ImageView avatarView) {
mContext = activity.getApplicationContext();
mAvatarView = avatarView;
mAvatarView.setOnClickListener(v -> {
if (hasAccount()) {
//TODO(b/117509285) launch the new page of the MeCard
} else {
final Intent intent = FeatureFactory.getFactory(mContext)
.getAccountFeatureProvider()
.getAccountSettingsDeeplinkIntent();
final Intent intent = FeatureFactory.getFactory(mContext)
.getAccountFeatureProvider()
.getAccountSettingsDeeplinkIntent();
if (intent != null) {
activity.startActivityForResult(intent, REQUEST_CODE);
}
if (intent == null) {
return;
}
if (!TextUtils.isEmpty(mAccountName)) {
//TODO(b/117509285) launch the new page of the MeCard
intent.putExtra(EXTRA_ACCOUNT_NAME, mAccountName);
}
// Here may have two different UI while start the activity.
// It will display adding account UI when device has no any account.
// It will display account information page when intent added the specified account.
activity.startActivityForResult(intent, REQUEST_CODE);
});
mAvatarImage = new MutableLiveData<>();
@@ -91,7 +101,7 @@ public class AvatarViewMixin implements LifecycleObserver {
return;
}
if (hasAccount()) {
loadAvatar();
loadAccount();
} else {
mAvatarView.setImageResource(R.drawable.ic_account_circle_24dp);
}
@@ -104,7 +114,7 @@ public class AvatarViewMixin implements LifecycleObserver {
return (accounts != null) && (accounts.length > 0);
}
private void loadAvatar() {
private void loadAccount() {
final String authority = queryProviderAuthority();
if (TextUtils.isEmpty(authority)) {
return;
@@ -117,6 +127,7 @@ public class AvatarViewMixin implements LifecycleObserver {
final Bundle bundle = mContext.getContentResolver().call(uri,
METHOD_GET_ACCOUNT_AVATAR, null /* arg */, null /* extras */);
final Bitmap bitmap = bundle.getParcelable(KEY_AVATAR_BITMAP);
mAccountName = bundle.getString(KEY_ACCOUNT_NAME, "" /* defaultValue */);
mAvatarImage.postValue(bitmap);
});
}

View File

@@ -17,7 +17,6 @@
package com.android.settings.biometrics.face;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
@@ -31,6 +30,7 @@ import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.util.Size;
import android.util.TypedValue;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
@@ -41,11 +41,7 @@ import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollSidecar;
import com.android.settings.core.InstrumentedPreferenceFragment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Fragment that contains the logic for showing and controlling the camera preview, circular
@@ -120,7 +116,6 @@ public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment
@Override
public void onOpened(CameraDevice cameraDevice) {
mCameraDevice = cameraDevice;
try {
// Configure the size of default buffer
SurfaceTexture texture = mTextureView.getSurfaceTexture();
@@ -245,11 +240,8 @@ public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment
/**
* Sets up member variables related to camera.
*
* @param width The width of available size for camera preview
* @param height The height of available size for camera preview
*/
private void setUpCameraOutputs(int width, int height) {
private void setUpCameraOutputs() {
try {
for (String cameraId : mCameraManager.getCameraIdList()) {
CameraCharacteristics characteristics =
@@ -265,8 +257,7 @@ public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment
// Get the stream configurations
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
width, height, MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT);
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class));
break;
}
} catch (CameraAccessException e) {
@@ -281,7 +272,7 @@ public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment
*/
private void openCamera(int width, int height) {
try {
setUpCameraOutputs(width, height);
setUpCameraOutputs();
mCameraManager.openCamera(mCameraId, mCameraStateCallback, mHandler);
configureTransform(width, height);
} catch (CameraAccessException e) {
@@ -292,35 +283,15 @@ public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment
/**
* Chooses the optimal resolution for the camera to open.
*/
private Size chooseOptimalSize(Size[] choices, int textureViewWidth, int textureViewHeight,
int maxWidth, int maxHeight) {
// Collect the supported resolutions that are at least as big as the preview Surface
List<Size> bigEnough = new ArrayList<>();
// Collect the supported resolutions that are smaller than the preview Surface
List<Size> notBigEnough = new ArrayList<>();
for (Size option : choices) {
if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
option.getHeight() == option.getWidth()) {
if (option.getWidth() >= textureViewWidth &&
option.getHeight() >= textureViewHeight) {
bigEnough.add(option);
} else {
notBigEnough.add(option);
}
private Size chooseOptimalSize(Size[] choices) {
for (int i = 0; i < choices.length; i++) {
if (choices[i].getHeight() == MAX_PREVIEW_HEIGHT
&& choices[i].getWidth() == MAX_PREVIEW_WIDTH) {
return choices[i];
}
}
// Pick the smallest of those big enough. If there is no one big enough, pick the
// largest of those not big enough.
if (bigEnough.size() > 0) {
return Collections.min(bigEnough, new CompareSizesByArea());
} else if (notBigEnough.size() > 0) {
return Collections.max(notBigEnough, new CompareSizesByArea());
} else {
Log.e(TAG, "Couldn't find any suitable preview size");
return choices[0];
}
Log.w(TAG, "Unable to find a good resolution");
return choices[0];
}
/**
@@ -337,19 +308,22 @@ public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment
}
// Fix the aspect ratio
Matrix matrix = new Matrix();
float scaleX = (float) viewWidth / mPreviewSize.getWidth();
float scaleY = (float) viewHeight / mPreviewSize.getHeight();
// Now divide by smaller one so it fills up the original space
// Now divide by smaller one so it fills up the original space.
float smaller = Math.min(scaleX, scaleY);
scaleX = scaleX / smaller;
scaleY = scaleY / smaller;
// Apply the scale
matrix.setScale(scaleX, scaleY);
// Apply the transformation/scale
mTextureView.setTranslationX(getResources().getDimension(R.dimen.face_preview_translate_x));
mTextureView.setTranslationY(getResources().getDimension(R.dimen.face_preview_translate_y));
mTextureView.setTransform(matrix);
final TypedValue scale = new TypedValue();
getResources().getValue(R.dimen.face_preview_scale, scale, true /* resolveRefs */);
mTextureView.setScaleX(scaleX * scale.getFloat());
mTextureView.setScaleY(scaleY * scale.getFloat());
}
private void closeCamera() {
@@ -362,18 +336,4 @@ public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment
mCameraDevice = null;
}
}
/**
* Compares two {@code Size}s based on their areas.
*/
private static class CompareSizesByArea implements Comparator<Size> {
@Override
public int compare(Size lhs, Size rhs) {
// We cast here to ensure the multiplications won't overflow
return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
(long) rhs.getWidth() * rhs.getHeight());
}
}
}

View File

@@ -17,6 +17,7 @@
package com.android.settings.homepage.contextualcards;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
@@ -36,10 +37,15 @@ import com.android.settingslib.utils.ThreadUtils;
*/
public class CardContentProvider extends ContentProvider {
private static final String TAG = "CardContentProvider";
public static final String CARD_AUTHORITY = "com.android.settings.homepage.CardContentProvider";
public static final Uri URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(CardContentProvider.CARD_AUTHORITY)
.appendPath(CardDatabaseHelper.CARD_TABLE)
.build();
private static final String TAG = "CardContentProvider";
/** URI matcher for ContentProvider queries. */
private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
/** URI matcher type for cards table */
@@ -98,17 +104,7 @@ public class CardContentProvider extends ContentProvider {
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
maybeEnableStrictMode();
final SQLiteDatabase database = mDBHelper.getWritableDatabase();
final String table = getTableFromMatch(uri);
final int rowsDeleted = database.delete(table, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null /* observer */);
return rowsDeleted;
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
throw new UnsupportedOperationException("delete operation not supported currently.");
}
@Override
@@ -140,18 +136,7 @@ public class CardContentProvider extends ContentProvider {
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
maybeEnableStrictMode();
final SQLiteDatabase database = mDBHelper.getWritableDatabase();
final String table = getTableFromMatch(uri);
final int rowsUpdated = database.update(table, values, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null /* observer */);
return rowsUpdated;
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
throw new UnsupportedOperationException("update operation not supported currently.");
}
@VisibleForTesting

View File

@@ -209,16 +209,19 @@ public class CardDatabaseHelper extends SQLiteOpenHelper {
* Mark a specific ContextualCard with dismissal flag in the database to indicate that the
* card has been dismissed.
*
* @param cardName the card name of the ContextualCard which is dismissed by user.
* @return updated row number
* @param context Context
* @param cardName The card name of the ContextualCard which is dismissed by user.
* @return The number of rows updated
*/
public int markContextualCardAsDismissed(String cardName) {
final SQLiteDatabase database = this.getWritableDatabase();
public int markContextualCardAsDismissed(Context context, String cardName) {
final SQLiteDatabase database = getWritableDatabase();
final ContentValues values = new ContentValues();
values.put(CardColumns.CARD_DISMISSED, 1);
final String selection = CardColumns.NAME + "=?";
final String[] selectionArgs = {cardName};
final int rowsUpdated = database.update(CARD_TABLE, values, selection, selectionArgs);
database.close();
context.getContentResolver().notifyChange(CardContentProvider.URI, null);
return rowsUpdated;
}
}

View File

@@ -24,8 +24,11 @@ import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -60,6 +63,19 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
mContext = context.getApplicationContext();
}
@Override
protected void onStartLoading() {
super.onStartLoading();
mContext.getContentResolver().registerContentObserver(CardContentProvider.URI,
false /*notifyForDescendants*/, mObserver);
}
@Override
protected void onStopLoading() {
super.onStopLoading();
mContext.getContentResolver().unregisterContentObserver(mObserver);
}
@Override
protected void onDiscardResult(List<ContextualCard> result) {
@@ -184,4 +200,14 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
}
return -1L;
}
private final ContentObserver mObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
if (isStarted()) {
forceLoad();
}
}
};
}

View File

@@ -114,7 +114,7 @@ public class ControllerRendererPool {
if (ConditionContextualCardController.class == clz) {
return new ConditionContextualCardController(context);
} else if (SliceContextualCardController.class == clz) {
return new SliceContextualCardController();
return new SliceContextualCardController(context);
} else if (LegacySuggestionContextualCardController.class == clz) {
return new LegacySuggestionContextualCardController(context);
}

View File

@@ -77,13 +77,13 @@ public class ConditionContextualCardController implements ContextualCardControll
@Override
public void onActionClick(ContextualCard contextualCard) {
final ConditionalContextualCard card = (ConditionalContextualCard) contextualCard;
mConditionManager.onActionClick(card.getConditionId());
}
@Override
public void onDismissed(ContextualCard contextualCard) {
final ConditionalContextualCard card = (ConditionalContextualCard) contextualCard;
mConditionManager.onActionClick(card.getConditionId());
}
@Override
@@ -93,8 +93,8 @@ public class ConditionContextualCardController implements ContextualCardControll
final boolean isOddNumber = conditionCards.size() % 2 == 1;
if (isOddNumber) {
final int lastIndex = conditionCards.size() - 1;
final ConditionalContextualCard card = (ConditionalContextualCard) conditionCards.get(
lastIndex);
final ConditionalContextualCard card = (ConditionalContextualCard) conditionCards
.get(lastIndex);
conditionCards.set(lastIndex, card.mutate().setIsHalfWidth(false).build());
}

View File

@@ -113,7 +113,7 @@ public class ConditionContextualCardRenderer implements ContextualCardRenderer {
viewContext, MetricsProto.MetricsEvent.ACTION_SETTINGS_CONDITION_BUTTON,
card.getMetricsConstant());
mControllerRendererPool.getController(mContext, card.getCardType())
.onDismissed(card);
.onActionClick(card);
});
} else {
button.setVisibility(View.GONE);

View File

@@ -16,9 +16,14 @@
package com.android.settings.homepage.contextualcards.slices;
import android.content.Context;
import com.android.settings.homepage.contextualcards.CardContentProvider;
import com.android.settings.homepage.contextualcards.CardDatabaseHelper;
import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ContextualCardController;
import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener;
import com.android.settingslib.utils.ThreadUtils;
/**
* Card controller for {@link ContextualCard} built as slices.
@@ -27,8 +32,13 @@ public class SliceContextualCardController implements ContextualCardController {
private static final String TAG = "SliceCardController";
private Context mContext;
private ContextualCardUpdateListener mCardUpdateListener;
public SliceContextualCardController(Context context) {
mContext = context;
}
@Override
public int getCardType() {
return ContextualCard.CardType.SLICE;
@@ -46,11 +56,14 @@ public class SliceContextualCardController implements ContextualCardController {
@Override
public void onDismissed(ContextualCard card) {
//TODO(b/113783548): Mark this card as dismissed in db and reload loader.
ThreadUtils.postOnBackgroundThread(() -> {
final CardDatabaseHelper dbHelper = CardDatabaseHelper.getInstance(mContext);
dbHelper.markContextualCardAsDismissed(mContext, card.getName());
});
}
@Override
public void setCardUpdateListener(ContextualCardUpdateListener listener) {
mCardUpdateListener = listener;
mCardUpdateListener = listener;
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.settings.wifi.qrcode;
import android.graphics.Bitmap;
import android.graphics.Color;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
public final class QrCodeGenerator {
/**
* Generates a barcode image with {@code contents}.
*
* @param contents The contents to encode in the barcode
* @param size The preferred image size in pixels
* @return Barcode bitmap
*/
public static Bitmap encodeQrCode(String contents, int size)
throws WriterException, IllegalArgumentException {
final BitMatrix qrBits = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE,
size, size);
final Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.RGB_565);
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
bitmap.setPixel(x, y, qrBits.get(x, y) ? Color.BLACK : Color.WHITE);
}
}
return bitmap;
}
}

View File

@@ -71,6 +71,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
private WifiManager mWifiManager;
private boolean mRestartWifiApAfterConfigChange;
private boolean mUnavailable;
@VisibleForTesting
TetherChangeReceiver mTetherChangeReceiver;
@@ -94,6 +95,15 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
return "WifiTetherSettings";
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setIfOnlyAvailableForAdmins(true);
if (isUiRestricted()) {
mUnavailable = true;
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
@@ -109,6 +119,9 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (mUnavailable) {
return;
}
// Assume we are in a SettingsActivity. This is only safe because we currently use
// SettingsActivity as base for all preference fragments.
final SettingsActivity activity = (SettingsActivity) getActivity();
@@ -122,6 +135,13 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
@Override
public void onStart() {
super.onStart();
if (mUnavailable) {
if (!isUiRestrictedByOnlyAdmin()) {
getEmptyTextView().setText(R.string.tethering_settings_not_available);
}
getPreferenceScreen().removeAll();
return;
}
final Context context = getContext();
if (context != null) {
context.registerReceiver(mTetherChangeReceiver, TETHER_STATE_CHANGE_FILTER);
@@ -131,6 +151,9 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
@Override
public void onStop() {
super.onStop();
if (mUnavailable) {
return;
}
final Context context = getContext();
if (context != null) {
context.unregisterReceiver(mTetherChangeReceiver);

View File

@@ -18,16 +18,25 @@ package com.android.settings.accounts;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.accounts.Account;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.widget.ImageView;
import com.android.settings.homepage.SettingsHomepageActivity;
@@ -44,6 +53,7 @@ import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowContentResolver;
import org.robolectric.shadows.ShadowPackageManager;
@RunWith(SettingsRobolectricTestRunner.class)
@@ -51,6 +61,7 @@ public class AvatarViewMixinTest {
private static final String DUMMY_ACCOUNT = "test@domain.com";
private static final String DUMMY_DOMAIN = "domain.com";
private static final String DUMMY_AUTHORITY = "authority.domain.com";
private static final String METHOD_GET_ACCOUNT_AVATAR = "getAccountAvatar";
private Context mContext;
private ImageView mImageView;
@@ -128,6 +139,31 @@ public class AvatarViewMixinTest {
assertThat(avatarViewMixin.queryProviderAuthority()).isEqualTo(DUMMY_AUTHORITY);
}
@Test
public void callWithGetAccountAvatarMethod_useDummyData_shouldReturnAccountNameAndAvatar() {
final ShadowContentResolver shadowContentResolver = Shadow.extract(
mContext.getContentResolver());
final Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(
DUMMY_AUTHORITY).build();
final ContentProvider mockContentProvider = mock(ContentProvider.class);
ShadowContentResolver.registerProviderInternal(DUMMY_AUTHORITY, mockContentProvider);
final Bundle bundle = new Bundle();
final Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
bundle.putParcelable("account_avatar", bitmap);
bundle.putString("account_name", DUMMY_ACCOUNT);
doReturn(bundle).when(mockContentProvider).call(anyString(), anyString(),
any(Bundle.class));
final Bundle expectBundle = shadowContentResolver.call(uri, METHOD_GET_ACCOUNT_AVATAR,
null /* arg */, null /* extras */);
final Object object = bundle.getParcelable("account_avatar");
assertThat(object instanceof Bitmap).isTrue();
assertThat(bundle.getString("account_name")).isEqualTo(DUMMY_ACCOUNT);
}
@Implements(value = AccountFeatureProviderImpl.class)
public static class ShadowAccountFeatureProviderImpl {

View File

@@ -16,6 +16,8 @@
package com.android.settings.applications.appinfo;
import static com.android.settings.core.FeatureFlags.DATA_USAGE_V2;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -33,6 +35,7 @@ import android.content.pm.ApplicationInfo;
import android.net.ConnectivityManager;
import android.net.INetworkStatsSession;
import android.os.Bundle;
import android.util.FeatureFlagUtils;
import androidx.loader.app.LoaderManager;
import androidx.preference.Preference;
@@ -65,6 +68,7 @@ public class AppDataUsagePreferenceControllerTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application.getApplicationContext());
FeatureFlagUtils.setEnabled(mContext, DATA_USAGE_V2, false);
mController = spy(new AppDataUsagePreferenceController(mContext, "test_key"));
mController.setParentFragment(mFragment);
}

View File

@@ -77,11 +77,6 @@ public class AppMemoryPreferenceControllerTest {
when(mFragment.getActivity()).thenReturn(mActivity);
}
@After
public void tearDown() {
ShadowUserManager.getShadow().reset();
}
@Test
@Config(qualifiers = "mcc999")
public void getAvailabilityStatus_developmentSettingsEnabled_shouldReturnAvailable() {

View File

@@ -28,6 +28,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.NetworkPolicyManager;
@@ -68,6 +69,8 @@ public class DataUsageSummaryTest {
private SummaryLoader mSummaryLoader;
@Mock
private NetworkPolicyManager mNetworkPolicyManager;
@Mock
private NetworkStatsManager mNetworkStatsManager;
private Context mContext;
private FragmentActivity mActivity;
private SummaryLoader.SummaryProvider mSummaryProvider;
@@ -86,16 +89,12 @@ public class DataUsageSummaryTest {
mContext = RuntimeEnvironment.application;
mActivity = spy(Robolectric.buildActivity(FragmentActivity.class).get());
doReturn(mNetworkStatsManager).when(mActivity).getSystemService(NetworkStatsManager.class);
mSummaryProvider = DataUsageSummary.SUMMARY_PROVIDER_FACTORY
.createSummaryProvider(mActivity, mSummaryLoader);
}
@After
public void tearDown() {
ShadowUserManager.getShadow().reset();
}
@Test
public void formatUsage_shouldLookLikeFormatFileSize() {
SettingsShadowResources.overrideResource(com.android.internal.R.string.fileSizeSuffix,

View File

@@ -75,7 +75,6 @@ public class DevelopmentSettingsDashboardFragmentTest {
@After
public void tearDown() {
ShadowEnableDevelopmentSettingWarningDialog.reset();
mShadowUserManager.reset();
}
@Test

View File

@@ -70,7 +70,6 @@ public class DevelopmentSwitchBarControllerTest {
@After
public void tearDown() {
ShadowUtils.reset();
ShadowUserManager.getShadow().reset();
}
@Test

View File

@@ -95,15 +95,12 @@ public class CardContentProviderTest {
assertThat(count).isGreaterThan(0);
}
@Test
@Test(expected = UnsupportedOperationException.class)
public void cardData_delete() {
mResolver.insert(mUri, generateOneRow());
final int delCount = mResolver.delete(mUri, null, null);
assertThat(delCount).isGreaterThan(0);
}
@Test
@Test(expected = UnsupportedOperationException.class)
public void cardData_update() {
mResolver.insert(mUri, generateOneRow());
@@ -113,16 +110,6 @@ public class CardContentProviderTest {
final String strWhere = CardDatabaseHelper.CardColumns.NAME + "=?";
final String[] selectionArgs = {"auto_rotate"};
final int updateCount = mResolver.update(mUri, values, strWhere, selectionArgs);
assertThat(updateCount).isGreaterThan(0);
final String[] columns = {CardDatabaseHelper.CardColumns.SCORE};
final Cursor cr = mResolver.query(mUri, columns, strWhere, selectionArgs, null);
cr.moveToFirst();
final double qryScore = cr.getDouble(0);
cr.close();
assertThat(qryScore).isEqualTo(updatingScore);
}
@Test
@@ -145,28 +132,6 @@ public class CardContentProviderTest {
verify(mProvider).enableStrictMode();
}
@Test
public void delete_isMainThread_shouldEnableStrictMode() {
ShadowThreadUtils.setIsMainThread(true);
ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true);
mProvider.delete(mUri, null, null);
verify(mProvider).enableStrictMode();
}
@Test
public void update_isMainThread_shouldEnableStrictMode() {
ShadowThreadUtils.setIsMainThread(true);
ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true);
final ContentValues values = new ContentValues();
values.put(CardDatabaseHelper.CardColumns.SCORE, "0.01");
mProvider.update(mUri, values, null, null);
verify(mProvider).enableStrictMode();
}
@Test
public void insert_notMainThread_shouldNotEnableStrictMode() {
ShadowThreadUtils.setIsMainThread(false);
@@ -187,28 +152,6 @@ public class CardContentProviderTest {
verify(mProvider, never()).enableStrictMode();
}
@Test
public void delete_notMainThread_shouldNotEnableStrictMode() {
ShadowThreadUtils.setIsMainThread(false);
ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true);
mProvider.delete(mUri, null, null);
verify(mProvider, never()).enableStrictMode();
}
@Test
public void update_notMainThread_shouldNotEnableStrictMode() {
ShadowThreadUtils.setIsMainThread(false);
ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true);
final ContentValues values = new ContentValues();
values.put(CardDatabaseHelper.CardColumns.SCORE, "0.01");
mProvider.update(mUri, values, null, null);
verify(mProvider, never()).enableStrictMode();
}
@Test(expected = UnsupportedOperationException.class)
public void getType_shouldCrash() {
mProvider.getType(null);

View File

@@ -0,0 +1,96 @@
/*
* 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.settings.homepage.contextualcards.slices;
import static com.google.common.truth.Truth.assertThat;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import com.android.settings.homepage.contextualcards.CardContentProvider;
import com.android.settings.homepage.contextualcards.CardDatabaseHelper;
import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowContentResolver;
@RunWith(SettingsRobolectricTestRunner.class)
public class SliceContextualCardControllerTest {
private static final String TEST_SLICE_URI = "content://test/test";
private static final String TEST_CARD_NAME = "test_card_name";
private Context mContext;
private CardContentProvider mProvider;
private ContentResolver mResolver;
private SliceContextualCardController mController;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mProvider = Robolectric.setupContentProvider(CardContentProvider.class);
ShadowContentResolver.registerProviderInternal(CardContentProvider.CARD_AUTHORITY,
mProvider);
mResolver = mContext.getContentResolver();
mController = new SliceContextualCardController(mContext);
}
@Test
public void onDismissed_cardShouldBeMarkedAsDismissed() {
final Uri providerUri = CardContentProvider.URI;
final ContextualCard card = new ContextualCard.Builder()
.setName(TEST_CARD_NAME)
.setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(Uri.parse(TEST_SLICE_URI))
.build();
mResolver.insert(providerUri, generateOneRow());
mController.onDismissed(card);
final String[] columns = {CardDatabaseHelper.CardColumns.CARD_DISMISSED};
final String selection = CardDatabaseHelper.CardColumns.NAME + "=?";
final String[] selectionArgs = {TEST_CARD_NAME};
final Cursor cr = mResolver.query(providerUri, columns, selection, selectionArgs, null);
cr.moveToFirst();
final int qryDismissed = cr.getInt(0);
cr.close();
assertThat(qryDismissed).isEqualTo(1);
}
private ContentValues generateOneRow() {
final ContentValues values = new ContentValues();
values.put(CardDatabaseHelper.CardColumns.NAME, TEST_CARD_NAME);
values.put(CardDatabaseHelper.CardColumns.TYPE, 1);
values.put(CardDatabaseHelper.CardColumns.SCORE, 0.9);
values.put(CardDatabaseHelper.CardColumns.SLICE_URI, TEST_SLICE_URI);
values.put(CardDatabaseHelper.CardColumns.CATEGORY, 2);
values.put(CardDatabaseHelper.CardColumns.PACKAGE_NAME, "com.android.settings");
values.put(CardDatabaseHelper.CardColumns.APP_VERSION, 10001);
values.put(CardDatabaseHelper.CardColumns.CARD_DISMISSED, 0);
return values;
}
}

View File

@@ -23,6 +23,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.Activity;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
@@ -48,6 +49,8 @@ public class DataUsagePreferenceControllerTest {
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private NetworkStatsManager mNetworkStatsManager;
private DataUsagePreferenceController mController;
private SwitchPreference mPreference;
private Context mContext;
@@ -59,6 +62,7 @@ public class DataUsagePreferenceControllerTest {
mContext = spy(Robolectric.setupActivity(Activity.class));
doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
doReturn(mNetworkStatsManager).when(mContext).getSystemService(NetworkStatsManager.class);
mPreference = new SwitchPreference(mContext);
mController = new DataUsagePreferenceController(mContext, "data_usage");

View File

@@ -48,7 +48,6 @@ public class RedactionInterstitialTest {
@After
public void tearDown() {
ShadowUserManager.getShadow().reset();
ShadowRestrictedLockUtilsInternal.reset();
}

View File

@@ -57,11 +57,6 @@ public class EncryptionStatusPreferenceControllerTest {
mPreference = new Preference(mContext);
}
@After
public void tearDown() {
mShadowUserManager.reset();
}
@Test
public void isAvailable_admin_true() {
mShadowUserManager.setIsAdminUser(true);

View File

@@ -63,11 +63,6 @@ public class RestrictedEncryptionPreferenceControllerTest {
mUserManager = ShadowUserManager.getShadow();
}
@After
public void tearDown() {
mUserManager.reset();
}
@Test
public void isAvailable_noRestriction_shouldReturnTrue() {
assertThat(mCredentialStoragePreferenceController.isAvailable()).isTrue();

View File

@@ -90,7 +90,6 @@ public class SlicesDatabaseAccessorTest {
@After
public void cleanUp() {
ShadowUserManager.getShadow().reset();
DatabaseTestUtils.clearDb(mContext);
}

View File

@@ -48,7 +48,6 @@ public class SystemDashboardFragmentTest {
@After
public void tearDown() {
SettingsShadowResources.reset();
ShadowUserManager.getShadow().reset();
}
@Test

View File

@@ -49,19 +49,6 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
private int[] profileIdsForUser = new int[0];
private boolean mUserSwitchEnabled;
@Resetter
public void reset() {
mUserInfos.clear();
mRestrictions.clear();
mUserProfileInfos.clear();
mRestrictionSources.clear();
mManagedProfiles.clear();
mIsQuietModeEnabled = false;
mUserSwitchEnabled = false;
profileIdsForUser = new int[0];
}
public void setUserInfo(int userHandle, UserInfo userInfo) {
mUserInfos.put(userHandle, userInfo);
}
@@ -90,7 +77,7 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
}
@Implementation
public List<UserHandle> getUserProfiles(){
public List<UserHandle> getUserProfiles() {
int[] userIds = getProfileIds(UserHandle.myUserId(), true /* enabledOnly */);
List<UserHandle> result = new ArrayList<>(userIds.length);
for (int userId : userIds) {

View File

@@ -63,11 +63,6 @@ public class AddUserWhenLockedPreferenceControllerTest {
mController = new AddUserWhenLockedPreferenceController(mContext, "fake_key");
}
@After
public void tearDown() {
mUserManager.reset();
}
@Test
public void displayPref_NotAdmin_shouldNotDisplay() {
mUserManager.setUserInfo(0, mUserInfo);

View File

@@ -105,11 +105,6 @@ public class EditUserInfoControllerTest {
mController = new TestEditUserInfoController();
}
@After
public void tearDown() {
mUserManager.reset();
}
@Test
public void photoControllerOnActivityResult_whenWaiting_isCalled() {
mController.createDialog(mFragment, mCurrentIcon, "test user",

View File

@@ -45,11 +45,6 @@ public class UserCapabilitiesTest {
mUserManager = ShadowUserManager.getShadow();
}
@After
public void tearDown() {
mUserManager.reset();
}
@Test
public void disallowUserSwitch_restrictionIsSet_true() {
mUserManager.setUserRestriction(UserHandle.of(UserHandle.myUserId()),

View File

@@ -30,13 +30,10 @@ import android.view.SurfaceHolder;
import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.LuminanceSource;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import java.util.concurrent.CountDownLatch;
@@ -111,36 +108,17 @@ public class QrCameraTest {
final String googleUrl = "http://www.google.com";
try {
Bitmap bmp = encodeQrCode(googleUrl, 320);
int[] intArray = new int[bmp.getWidth() * bmp.getHeight()];
final Bitmap bmp = QrCodeGenerator.encodeQrCode(googleUrl, 320);
final int[] intArray = new int[bmp.getWidth() * bmp.getHeight()];
bmp.getPixels(intArray, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight());
LuminanceSource source = new RGBLuminanceSource(bmp.getWidth(), bmp.getHeight(),
intArray);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
final BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
mCamera.decodeImage(bitmap);
bmp.recycle();
} catch (WriterException e) {
}
assertThat(mQrCode).isEqualTo(googleUrl);
}
private Bitmap encodeQrCode(String qrCode, int size) throws WriterException {
BitMatrix qrBits = null;
try {
qrBits =
new MultiFormatWriter().encode(qrCode, BarcodeFormat.QR_CODE, size, size, null);
} catch (IllegalArgumentException iae) {
// Should never reach here.
}
assertThat(qrBits).isNotNull();
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.RGB_565);
for (int x = 0; x < size; ++x) {
for (int y = 0; y < size; ++y) {
bitmap.setPixel(x, y, qrBits.get(x, y) ? Color.BLACK : Color.WHITE);
}
}
return bitmap;
}
}

View File

@@ -18,15 +18,24 @@ package com.android.settings.wifi.tether;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.nullable;
import static org.mockito.Mockito.doNothing;
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.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.widget.TextView;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowWifiManager;
@@ -37,10 +46,14 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceScreen;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = {ShadowWifiManager.class})
public class WifiTetherSettingsTest {
@@ -98,6 +111,31 @@ public class WifiTetherSettingsTest {
.isNotEmpty();
}
@Test
public void startFragment_notAdminUser_shouldRemoveAllPreferences() {
final WifiTetherSettings settings = spy(new WifiTetherSettings());
final FragmentActivity activity = mock(FragmentActivity.class);
when(settings.getActivity()).thenReturn(activity);
when(settings.getContext()).thenReturn(mContext);
final Resources.Theme theme = mContext.getTheme();
when(activity.getTheme()).thenReturn(theme);
when(activity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
doNothing().when(settings)
.onCreatePreferences(any(Bundle.class), nullable(String.class));
final FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest();
ReflectionHelpers.setField(settings, "mDashboardFeatureProvider",
fakeFeatureFactory.dashboardFeatureProvider);
final TextView emptyTextView = mock(TextView.class);
ReflectionHelpers.setField(settings, "mEmptyTextView", emptyTextView);
final PreferenceScreen screen = mock(PreferenceScreen.class);
doReturn(screen).when(settings).getPreferenceScreen();
settings.onCreate(Bundle.EMPTY);
settings.onStart();
verify(screen).removeAll();
}
private void setupIsTetherAvailable(boolean returnValue) {
when(mConnectivityManager.isTetheringSupported()).thenReturn(true);