From 6412dc4a8f47b134af6c5abb5f7f30321bb3ce68 Mon Sep 17 00:00:00 2001 From: Anthony Hugh Date: Mon, 14 Sep 2015 15:43:25 -0700 Subject: Permissions Watch settings UI port This CL contains all the UI elements needed to build the Wear version of the Settings UI for permissions. The actual implementation for the Wear version of SettingsUI will be done in a follow up bug. This is basically a straight port from our Settings app and wearable-support lib. I tweaked two strings and changed the package paths to make it easier to group the files together. BUG: 23080561 Change-Id: I6d72d29dd1926ad07d63f85b52ccf9f72ff70df1 --- .../ExtendedOnCenterProximityListener.java | 30 +++ .../ui/wear/settings/ExtendedViewHolder.java | 84 ++++++ .../ui/wear/settings/SettingsAdapter.java | 295 +++++++++++++++++++++ .../permission/ui/wear/settings/ViewUtils.java | 48 ++++ 4 files changed, 457 insertions(+) create mode 100644 src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedOnCenterProximityListener.java create mode 100644 src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedViewHolder.java create mode 100644 src/com/android/packageinstaller/permission/ui/wear/settings/SettingsAdapter.java create mode 100644 src/com/android/packageinstaller/permission/ui/wear/settings/ViewUtils.java (limited to 'src/com') diff --git a/src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedOnCenterProximityListener.java b/src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedOnCenterProximityListener.java new file mode 100644 index 00000000..02c203b3 --- /dev/null +++ b/src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedOnCenterProximityListener.java @@ -0,0 +1,30 @@ +/* + * 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. + */ + +package com.android.packageinstaller.permission.ui.wear.settings; + +import android.support.wearable.view.WearableListView; + +public interface ExtendedOnCenterProximityListener + extends WearableListView.OnCenterProximityListener { + float getProximityMinValue(); + + float getProximityMaxValue(); + + float getCurrentProximityValue(); + + void setScalingAnimatorValue(float value); +} diff --git a/src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedViewHolder.java b/src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedViewHolder.java new file mode 100644 index 00000000..6b725419 --- /dev/null +++ b/src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedViewHolder.java @@ -0,0 +1,84 @@ +/* + * 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. + */ + +package com.android.packageinstaller.permission.ui.wear.settings; + +import android.animation.ObjectAnimator; +import android.support.wearable.view.WearableListView; +import android.view.View; + + +public class ExtendedViewHolder extends WearableListView.ViewHolder { + public static final long DEFAULT_ANIMATION_DURATION = 150; + + private ObjectAnimator mScalingUpAnimator; + + private ObjectAnimator mScalingDownAnimator; + + private float mMinValue; + + private float mMaxValue; + + public ExtendedViewHolder(View itemView) { + super(itemView); + if (itemView instanceof ExtendedOnCenterProximityListener) { + ExtendedOnCenterProximityListener item = + (ExtendedOnCenterProximityListener) itemView; + mMinValue = item.getProximityMinValue(); + item.setScalingAnimatorValue(mMinValue); + mMaxValue = item.getProximityMaxValue(); + mScalingUpAnimator = ObjectAnimator.ofFloat(item, "scalingAnimatorValue", mMinValue, + mMaxValue); + mScalingUpAnimator.setDuration(DEFAULT_ANIMATION_DURATION); + mScalingDownAnimator = ObjectAnimator.ofFloat(item, "scalingAnimatorValue", + mMaxValue, mMinValue); + mScalingDownAnimator.setDuration(DEFAULT_ANIMATION_DURATION); + } + } + + public void onCenterProximity(boolean isCentralItem, boolean animate) { + if (!(itemView instanceof ExtendedOnCenterProximityListener)) { + return; + } + ExtendedOnCenterProximityListener item = (ExtendedOnCenterProximityListener) itemView; + if (isCentralItem) { + if (animate) { + mScalingDownAnimator.cancel(); + if (!mScalingUpAnimator.isRunning()) { + mScalingUpAnimator.setFloatValues(item.getCurrentProximityValue(), + mMaxValue); + mScalingUpAnimator.start(); + } + } else { + mScalingUpAnimator.cancel(); + item.setScalingAnimatorValue(item.getProximityMaxValue()); + } + } else { + mScalingUpAnimator.cancel(); + if (animate) { + if (!mScalingDownAnimator.isRunning()) { + mScalingDownAnimator.setFloatValues(item.getCurrentProximityValue(), + mMinValue); + mScalingDownAnimator.start(); + } + } else { + mScalingDownAnimator.cancel(); + item.setScalingAnimatorValue(item.getProximityMinValue()); + } + } + super.onCenterProximity(isCentralItem, animate); + } +} diff --git a/src/com/android/packageinstaller/permission/ui/wear/settings/SettingsAdapter.java b/src/com/android/packageinstaller/permission/ui/wear/settings/SettingsAdapter.java new file mode 100644 index 00000000..9216cbbf --- /dev/null +++ b/src/com/android/packageinstaller/permission/ui/wear/settings/SettingsAdapter.java @@ -0,0 +1,295 @@ +/* + * 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. + */ + +package com.android.packageinstaller.permission.ui.wear.settings; + +import android.content.Context; +import android.support.wearable.view.CircledImageView; +import android.support.wearable.view.WearableListView; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.TextAppearanceSpan; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.android.packageinstaller.R; + +import java.util.ArrayList; + +/** + * Common adapter for settings views. Maintains a list of 'Settings', consisting of a name, + * icon and optional activity-specific data. + */ +public class SettingsAdapter extends WearableListView.Adapter { + private static final String TAG = "SettingsAdapter"; + private final Context mContext; + + protected static CharSequence generateLabelWithState( + Context context, int labelId, boolean enabled) { + return generateLabelWithState(context, labelId, R.string.generic_enabled, enabled); + } + + protected static CharSequence generateLabelWithState( + Context context, int labelId, int onId, boolean enabled) { + SpannableStringBuilder ssb = new SpannableStringBuilder(context.getString(labelId)); + ssb.append('\n'); + ssb.append( + context.getString(enabled ? onId : R.string.generic_disabled), + new TextAppearanceSpan(context, R.style.TextAppearance_Settings_Label_Large), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + return ssb; + } + + public static final class Setting { + public static final int ID_INVALID = -1; + + public final int id; + public int nameResourceId; + public CharSequence name; + public int iconResource; + public boolean inProgress; + public S data; + + public Setting(CharSequence name, int iconResource, S data) { + this(name, iconResource, data, ID_INVALID); + } + + public Setting(CharSequence name, int iconResource, S data, int id) { + this.name = name; + this.iconResource = iconResource; + this.data = data; + this.inProgress = false; + this.id = id; + } + + public Setting(int nameResource, int iconResource, S data, int id) { + this.nameResourceId = nameResource; + this.iconResource = iconResource; + this.data = data; + this.inProgress = false; + this.id = id; + } + + public Setting(int nameResource, int iconResource, int id) { + this.nameResourceId = nameResource; + this.iconResource = iconResource; + this.data = null; + this.inProgress = false; + this.id = id; + } + + public Setting(CharSequence name, int iconResource, int id) { + this(name, iconResource, null, id); + } + + } + + private final int mItemLayoutId; + private final float mDefaultCircleRadiusPercent; + private final float mSelectedCircleRadiusPercent; + + protected ArrayList> mSettings = new ArrayList>(); + + public SettingsAdapter(Context context, int itemLayoutId) { + mContext = context; + mItemLayoutId = itemLayoutId; + mDefaultCircleRadiusPercent = context.getResources().getFraction( + R.dimen.default_settings_circle_radius_percent, 1, 1); + mSelectedCircleRadiusPercent = context.getResources().getFraction( + R.dimen.selected_settings_circle_radius_percent, 1, 1); + } + + @Override + public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new SettingsItemHolder(new SettingsItem(parent.getContext())); + } + + @Override + public void onBindViewHolder(WearableListView.ViewHolder holder, int position) { + Setting setting = mSettings.get(position); + if (setting.iconResource == -1) { + ((SettingsItemHolder) holder).imageView.setVisibility(View.GONE); + } else { + ((SettingsItemHolder) holder).imageView.setVisibility(View.VISIBLE); + ((SettingsItemHolder) holder).imageView.setImageResource( + mSettings.get(position).iconResource); + } + Log.d(TAG, "onBindViewHolder " + setting.name + " " + setting.id + " " + setting + .nameResourceId); + if (setting.name == null && setting.nameResourceId != 0) { + setting.name = mContext.getString(setting.nameResourceId); + } + ((SettingsItemHolder) holder).textView.setText(setting.name); + } + + @Override + public int getItemCount() { + return mSettings.size(); + } + + public void addSetting(CharSequence name, int iconResource) { + addSetting(name, iconResource, null); + } + + public void addSetting(CharSequence name, int iconResource, T intent) { + addSetting(mSettings.size(), name, iconResource, intent); + } + + public void addSetting(int index, CharSequence name, int iconResource, T intent) { + addSetting(Setting.ID_INVALID, index, name, iconResource, intent); + } + + public void addSetting(int id, int index, CharSequence name, int iconResource, T intent) { + mSettings.add(index, new Setting(name, iconResource, intent, id)); + notifyItemInserted(index); + } + + public void addSettingDontNotify(Setting setting) { + mSettings.add(setting); + } + + public void addSetting(Setting setting) { + mSettings.add(setting); + notifyItemInserted(mSettings.size() - 1); + } + + public void addSetting(int index, Setting setting) { + mSettings.add(index, setting); + notifyItemInserted(index); + } + + /** + * Returns the index of the setting in the adapter based on the ID supplied when it was + * originally added. + * @param id the setting's id + * @return index in the adapter of the setting. -1 if not found. + */ + public int findSetting(int id) { + for (int i = mSettings.size() - 1; i >= 0; --i) { + Setting setting = mSettings.get(i); + + if (setting.id == id) { + return i; + } + } + + return -1; + } + + /** + * Removes a setting at the given index. + * @param index the index of the setting to be removed + */ + public void removeSetting(int index) { + mSettings.remove(index); + notifyDataSetChanged(); + } + + public void clearSettings() { + mSettings.clear(); + notifyDataSetChanged(); + } + + /** + * Updates a setting in place. + * @param index the index of the setting + * @param name the updated setting name + * @param iconResource the update setting icon + * @param intent the updated intent for the setting + */ + public void updateSetting(int index, CharSequence name, int iconResource, T intent) { + Setting setting = mSettings.get(index); + setting.iconResource = iconResource; + setting.name = name; + setting.data = intent; + notifyItemChanged(index); + } + + public Setting get(int position) { + return mSettings.get(position); + } + + protected static class SettingsItemHolder extends ExtendedViewHolder { + public final CircledImageView imageView; + final TextView textView; + + public SettingsItemHolder(View itemView) { + super(itemView); + + imageView = ((CircledImageView) itemView.findViewById(R.id.image)); + textView = ((TextView) itemView.findViewById(R.id.text)); + } + } + + protected class SettingsItem extends FrameLayout implements ExtendedOnCenterProximityListener { + + private final CircledImageView mImage; + private final TextView mText; + + public SettingsItem(Context context) { + super(context); + View view = View.inflate(context, mItemLayoutId, null); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT); + params.gravity = Gravity.CENTER_VERTICAL; + addView(view, params); + mImage = (CircledImageView) findViewById(R.id.image); + mText = (TextView) findViewById(R.id.text); + } + + @Override + public float getProximityMinValue() { + return mDefaultCircleRadiusPercent; + } + + @Override + public float getProximityMaxValue() { + return mSelectedCircleRadiusPercent; + } + + @Override + public float getCurrentProximityValue() { + return mImage.getCircleRadiusPressedPercent(); + } + + @Override + public void setScalingAnimatorValue(float value) { + mImage.setCircleRadiusPercent(value); + mImage.setCircleRadiusPressedPercent(value); + } + + @Override + public void onCenterPosition(boolean animate) { + mImage.setAlpha(1f); + mText.setAlpha(1f); + } + + @Override + public void onNonCenterPosition(boolean animate) { + mImage.setAlpha(0.5f); + mText.setAlpha(0.5f); + } + + TextView getTextView() { + return mText; + } + } +} diff --git a/src/com/android/packageinstaller/permission/ui/wear/settings/ViewUtils.java b/src/com/android/packageinstaller/permission/ui/wear/settings/ViewUtils.java new file mode 100644 index 00000000..cf1c0fd0 --- /dev/null +++ b/src/com/android/packageinstaller/permission/ui/wear/settings/ViewUtils.java @@ -0,0 +1,48 @@ +/* + * 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. + */ + +package com.android.packageinstaller.permission.ui.wear.settings; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; + +/** + * Utility to determine screen shape + */ +public class ViewUtils { + + public static boolean getIsCircular(Context context) { + return context.getResources().getConfiguration().isScreenRound(); + } + + /** + * Set the given {@code view} and all descendants to the given {@code enabled} state. + * + * @param view the parent view of a subtree of components whose enabled state must be set + * @param enabled the new enabled state of the subtree of components + */ + public static void setEnabled(View view, boolean enabled) { + view.setEnabled(enabled); + + if (view instanceof ViewGroup) { + final ViewGroup viewGroup = (ViewGroup) view; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + setEnabled(viewGroup.getChildAt(i), enabled); + } + } + } +} -- cgit v1.2.3