diff --git a/res/drawable/ic_call_24dp.xml b/res/drawable/ic_call_24dp.xml new file mode 100644 index 00000000000..950547a917b --- /dev/null +++ b/res/drawable/ic_call_24dp.xml @@ -0,0 +1,31 @@ + + + + + + \ No newline at end of file diff --git a/res/drawable/ic_chat_24dp.xml b/res/drawable/ic_chat_24dp.xml new file mode 100644 index 00000000000..2df12b37f98 --- /dev/null +++ b/res/drawable/ic_chat_24dp.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/res/drawable/ic_mail_24dp.xml b/res/drawable/ic_mail_24dp.xml new file mode 100644 index 00000000000..46f45842797 --- /dev/null +++ b/res/drawable/ic_mail_24dp.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/res/layout/support_escalation_card.xml b/res/layout/support_escalation_card.xml index 3ca7562a12e..e2f9fe46490 100644 --- a/res/layout/support_escalation_card.xml +++ b/res/layout/support_escalation_card.xml @@ -17,29 +17,30 @@ + android:layout_marginTop="4dp" + android:clickable="true" + android:foreground="?android:attr/selectableItemBackground"> + android:orientation="horizontal"> + - \ No newline at end of file diff --git a/res/layout/support_fragment.xml b/res/layout/support_fragment.xml index d81849d5923..481a5487bc2 100644 --- a/res/layout/support_fragment.xml +++ b/res/layout/support_fragment.xml @@ -15,53 +15,8 @@ limitations under the License. --> - - - - - - - - - - - - - - \ No newline at end of file + android:layout_height="match_parent"/> \ No newline at end of file diff --git a/res/layout/support_item_subtitle.xml b/res/layout/support_item_subtitle.xml new file mode 100644 index 00000000000..62b2fc1893c --- /dev/null +++ b/res/layout/support_item_subtitle.xml @@ -0,0 +1,27 @@ + + + + \ No newline at end of file diff --git a/res/layout/support_item_title.xml b/res/layout/support_item_title.xml new file mode 100644 index 00000000000..eff3f7fda96 --- /dev/null +++ b/res/layout/support_item_title.xml @@ -0,0 +1,40 @@ + + + + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index b7fc2c2f27f..388f90d05c7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7469,8 +7469,14 @@ Free up %1$s - - 24/7 support + + Around the clock help + + + Call or email us and we\'ll get your issue solved right away. No muss, no fuss. + + + More help Phone @@ -7478,6 +7484,9 @@ Email + + Chat + Help forum diff --git a/res/values/styles.xml b/res/values/styles.xml index 6601325a75a..345edc9733c 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -344,6 +344,14 @@ @color/warning + + + diff --git a/src/com/android/settings/dashboard/DashboardContainerFragment.java b/src/com/android/settings/dashboard/DashboardContainerFragment.java index 831ac508f63..b75cef2f80e 100644 --- a/src/com/android/settings/dashboard/DashboardContainerFragment.java +++ b/src/com/android/settings/dashboard/DashboardContainerFragment.java @@ -99,7 +99,7 @@ public final class DashboardContainerFragment extends InstrumentedFragment { super(fragmentManager); mContext = context; mSupportFeatureProvider = - FeatureFactory.getFactory(context).getSupportFeatureProvider(); + FeatureFactory.getFactory(context).getSupportFeatureProvider(context); } @Override diff --git a/src/com/android/settings/dashboard/SupportFragment.java b/src/com/android/settings/dashboard/SupportFragment.java index c4d74efb005..eb85fefe2ed 100644 --- a/src/com/android/settings/dashboard/SupportFragment.java +++ b/src/com/android/settings/dashboard/SupportFragment.java @@ -16,16 +16,16 @@ package com.android.settings.dashboard; -import android.annotation.DrawableRes; -import android.annotation.IdRes; -import android.annotation.StringRes; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.OnAccountsUpdateListener; import android.app.Activity; import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; import com.android.settings.InstrumentedFragment; import com.android.settings.R; @@ -35,10 +35,14 @@ import com.android.settings.overlay.SupportFeatureProvider; /** * Fragment for support tab in SettingsGoogle. */ -public final class SupportFragment extends InstrumentedFragment implements View.OnClickListener { +public final class SupportFragment extends InstrumentedFragment implements View.OnClickListener, + OnAccountsUpdateListener { private Activity mActivity; private View mContent; + private RecyclerView mRecyclerView; + private SupportItemAdapter mSupportItemAdapter; + private AccountManager mAccountManager; private SupportFeatureProvider mSupportFeatureProvider; @Override @@ -50,45 +54,49 @@ public final class SupportFragment extends InstrumentedFragment implements View. public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mActivity = getActivity(); + mAccountManager = AccountManager.get(mActivity); mSupportFeatureProvider = - FeatureFactory.getFactory(getContext()).getSupportFeatureProvider(); + FeatureFactory.getFactory(mActivity).getSupportFeatureProvider(mActivity); + mSupportItemAdapter = new SupportItemAdapter(mActivity, mSupportFeatureProvider, + this /* itemClickListener */); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mContent = inflater.inflate(R.layout.support_fragment, container, false); - // Update escalation items. - updateEscalationCard(R.id.escalation_by_phone, R.string.support_escalation_by_phone); - updateEscalationCard(R.id.escalation_by_email, R.string.support_escalation_by_email); - // Update other support items. - updateSupportTile(R.id.forum_tile, R.drawable.ic_forum_24dp, R.string.support_forum_title); - updateSupportTile(R.id.article_tile, R.drawable.ic_help_24dp, - R.string.support_articles_title); - // Update feedback item. - updateSupportTile(R.id.feedback_tile, R.drawable.ic_feedback_24dp, - R.string.support_feedback_title); + mRecyclerView = (RecyclerView) mContent.findViewById(R.id.support_items); + mRecyclerView.setLayoutManager(new LinearLayoutManager( + getActivity(), LinearLayoutManager.VERTICAL, false /* reverseLayout */)); + mRecyclerView.setAdapter(mSupportItemAdapter); return mContent; } - private void updateEscalationCard(@IdRes int cardId, @StringRes int title) { - final View card = mContent.findViewById(cardId); - ((TextView) card.findViewById(R.id.title)).setText(title); + @Override + public void onResume() { + super.onResume(); + // Monitor account change. + mAccountManager.addOnAccountsUpdatedListener( + this /* listener */, null /* handler */, true /* updateImmediately */); } - private void updateSupportTile(@IdRes int tileId, @DrawableRes int icon, @StringRes int title) { - final View tile = mContent.findViewById(tileId); - ((ImageView) tile.findViewById(android.R.id.icon)).setImageResource(icon); - ((TextView) tile.findViewById(android.R.id.title)).setText(title); - tile.setOnClickListener(this); + @Override + public void onPause() { + super.onPause(); + // Stop monitor account change. + mAccountManager.removeOnAccountsUpdatedListener(this /* listener */); + } + + @Override + public void onAccountsUpdated(Account[] accounts) { + // Account changed, update support items. + mSupportItemAdapter.refreshData(); } @Override public void onClick(View v) { - switch (v.getId()) { - case R.id.forum_tile: - mActivity.startActivity(mSupportFeatureProvider.getForumIntent()); - break; - } + final SupportItemAdapter.ViewHolder vh = + (SupportItemAdapter.ViewHolder) mRecyclerView.getChildViewHolder(v); + mSupportItemAdapter.onItemClicked(vh.getAdapterPosition()); } } diff --git a/src/com/android/settings/dashboard/SupportItemAdapter.java b/src/com/android/settings/dashboard/SupportItemAdapter.java new file mode 100644 index 00000000000..867b38c5b0f --- /dev/null +++ b/src/com/android/settings/dashboard/SupportItemAdapter.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2016 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.dashboard; + +import android.accounts.Account; +import android.annotation.DrawableRes; +import android.annotation.LayoutRes; +import android.annotation.StringRes; +import android.app.Activity; +import android.content.Intent; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.settings.R; +import com.android.settings.overlay.SupportFeatureProvider; + +import java.util.ArrayList; +import java.util.List; + +import static com.android.settings.overlay.SupportFeatureProvider.SupportType.CHAT; +import static com.android.settings.overlay.SupportFeatureProvider.SupportType.EMAIL; +import static com.android.settings.overlay.SupportFeatureProvider.SupportType.PHONE; + +/** + * Item adapter for support tiles. + */ +public final class SupportItemAdapter extends RecyclerView.Adapter { + + private static final int TYPE_TITLE = R.layout.support_item_title; + private static final int TYPE_SUBTITLE = R.layout.support_item_subtitle; + private static final int TYPE_ESCALATION_CARD = R.layout.support_escalation_card; + private static final int TYPE_SUPPORT_TILE = R.layout.support_tile; + + private final Activity mActivity; + private final SupportFeatureProvider mSupportFeatureProvider; + private final View.OnClickListener mItemClickListener; + private final List mSupportData; + + public SupportItemAdapter(Activity activity, SupportFeatureProvider supportFeatureProvider, + View.OnClickListener itemClickListener) { + mActivity = activity; + mSupportFeatureProvider = supportFeatureProvider; + mItemClickListener = itemClickListener; + mSupportData = new ArrayList<>(); + setHasStableIds(true); + refreshData(); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate( + viewType, parent, false)); + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + final SupportData data = mSupportData.get(position); + if (holder.iconView != null) { + holder.iconView.setImageResource(data.icon); + } + if (holder.titleView != null) { + holder.titleView.setText(data.title); + } + if (holder.summaryView != null) { + holder.summaryView.setText(data.summary); + } + holder.itemView.setOnClickListener(mItemClickListener); + } + + @Override + public int getItemViewType(int position) { + return mSupportData.get(position).type; + } + + @Override + public int getItemCount() { + return mSupportData.size(); + } + + /** + * Called when a support item is clicked. + */ + public void onItemClicked(int position) { + if (position >= 0 && position < mSupportData.size()) { + final SupportData data = mSupportData.get(position); + if (data.intent != null) { + mActivity.startActivityForResult(data.intent, 0); + } + } + } + + /** + * Create data for the adapter. If there is already data in the adapter, they will be + * destroyed and recreated. + */ + public void refreshData() { + mSupportData.clear(); + final Account[] accounts = mSupportFeatureProvider.getSupportEligibleAccounts(mActivity); + if (accounts.length > 0) { + addEscalationCards(accounts[0]); + } + addMoreHelpItems(); + notifyDataSetChanged(); + } + + private void addEscalationCards(Account account) { + mSupportData.add(new SupportData(TYPE_TITLE, 0 /* icon */, + R.string.support_escalation_title, R.string.support_escalation_summary, + null /* intent */)); + if (mSupportFeatureProvider.isSupportTypeEnabled(mActivity, PHONE)) { + mSupportData.add(new SupportData(TYPE_ESCALATION_CARD, R.drawable.ic_call_24dp, + R.string.support_escalation_by_phone, 0 /* summary */, + mSupportFeatureProvider.getSupportIntent(mActivity, account, PHONE))); + } + if (mSupportFeatureProvider.isSupportTypeEnabled(mActivity, EMAIL)) { + mSupportData.add(new SupportData(TYPE_ESCALATION_CARD, R.drawable.ic_mail_24dp, + R.string.support_escalation_by_email, 0 /* summary */, + mSupportFeatureProvider.getSupportIntent(mActivity, account, EMAIL))); + } + if (mSupportFeatureProvider.isSupportTypeEnabled(mActivity, CHAT)) { + mSupportData.add(new SupportData(TYPE_ESCALATION_CARD, R.drawable.ic_chat_24dp, + R.string.support_escalation_by_chat, 0 /* summary */, + mSupportFeatureProvider.getSupportIntent(mActivity, account, CHAT))); + } + } + + private void addMoreHelpItems() { + mSupportData.add(new SupportData(TYPE_SUBTITLE, 0 /* icon */, + R.string.support_more_help_title, 0 /* summary */, null /* intent */)); + mSupportData.add(new SupportData(TYPE_SUPPORT_TILE, R.drawable.ic_forum_24dp, + R.string.support_forum_title, 0 /* summary */, + mSupportFeatureProvider.getForumIntent())); + mSupportData.add(new SupportData(TYPE_SUPPORT_TILE, R.drawable.ic_help_24dp, + R.string.support_articles_title, 0 /* summary */, null /*intent */)); + mSupportData.add(new SupportData(TYPE_SUPPORT_TILE, R.drawable.ic_feedback_24dp, + R.string.support_feedback_title, 0 /* summary */, null /*intent */)); + } + + /** + * {@link RecyclerView.ViewHolder} for support items. + */ + static final class ViewHolder extends RecyclerView.ViewHolder { + + final ImageView iconView; + final TextView titleView; + final TextView summaryView; + + ViewHolder(View itemView) { + super(itemView); + iconView = (ImageView) itemView.findViewById(android.R.id.icon); + titleView = (TextView) itemView.findViewById(android.R.id.title); + summaryView = (TextView) itemView.findViewById(android.R.id.summary); + } + } + + /** + * Data for a single support item. + */ + private static final class SupportData { + + final Intent intent; + @LayoutRes final int type; + @DrawableRes final int icon; + @StringRes final int title; + @StringRes final int summary; + + SupportData(@LayoutRes int type, @DrawableRes int icon, @StringRes int title, + @StringRes int summary, Intent intent) { + this.type = type; + this.icon = icon; + this.title = title; + this.summary = summary; + this.intent = intent; + } + } +} diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java index 4dc9ba2eb45..1bffc2b2ab0 100644 --- a/src/com/android/settings/overlay/FeatureFactory.java +++ b/src/com/android/settings/overlay/FeatureFactory.java @@ -59,7 +59,7 @@ public abstract class FeatureFactory { return sFactory; } - public abstract SupportFeatureProvider getSupportFeatureProvider(); + public abstract SupportFeatureProvider getSupportFeatureProvider(Context context); public static final class FactoryNotFoundException extends RuntimeException { public FactoryNotFoundException(Throwable throwable) { diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index d884080a823..ce561f3d17d 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -16,13 +16,15 @@ package com.android.settings.overlay; +import android.content.Context; + /** * {@link FeatureFactory} implementation for AOSP Settings. */ public final class FeatureFactoryImpl extends FeatureFactory { @Override - public SupportFeatureProvider getSupportFeatureProvider() { + public SupportFeatureProvider getSupportFeatureProvider(Context context) { return null; } diff --git a/src/com/android/settings/overlay/SupportFeatureProvider.java b/src/com/android/settings/overlay/SupportFeatureProvider.java index 71f08768d45..299eaad6b23 100644 --- a/src/com/android/settings/overlay/SupportFeatureProvider.java +++ b/src/com/android/settings/overlay/SupportFeatureProvider.java @@ -16,16 +16,51 @@ package com.android.settings.overlay; +import android.accounts.Account; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.content.Context; import android.content.Intent; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Feature provider for support tab. */ public interface SupportFeatureProvider { + @IntDef({SupportType.EMAIL, SupportType.PHONE, SupportType.CHAT}) + @Retention(RetentionPolicy.SOURCE) + @interface SupportType { + int EMAIL = 1; + int PHONE = 2; + int CHAT = 3; + } + /** * Returns a intent that will open help forum. */ Intent getForumIntent(); + /** + * Whether or not a support type is enabled. + */ + boolean isSupportTypeEnabled(Context context, @SupportType int type); + + /** + * Returns an {@link Account} that's eligible for support options. + */ + @NonNull + Account[] getSupportEligibleAccounts(Context context); + + /** + * Returns an {@link Intent} that opens email support for specified account. + * + * @param context A UI Context + * @param account A account returned by {@link #getSupportEligibleAccounts} + * @param type The type of support account needs. + */ + Intent getSupportIntent(Context context, Account account, @SupportType int type); + }