1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.homepage.contextualcards; 18 19 import static com.android.settings.homepage.contextualcards.ContextualCardsAdapter.SPAN_COUNT; 20 21 import android.app.settings.SettingsEnums; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.os.Build; 27 import android.os.Bundle; 28 import android.util.Log; 29 import android.view.LayoutInflater; 30 import android.view.View; 31 import android.view.ViewGroup; 32 33 import androidx.annotation.VisibleForTesting; 34 import androidx.loader.app.LoaderManager; 35 import androidx.recyclerview.widget.GridLayoutManager; 36 import androidx.recyclerview.widget.ItemTouchHelper; 37 38 import com.android.settings.R; 39 import com.android.settings.core.InstrumentedFragment; 40 import com.android.settings.homepage.contextualcards.slices.BluetoothUpdateWorker; 41 import com.android.settings.homepage.contextualcards.slices.SwipeDismissalDelegate; 42 import com.android.settings.overlay.FeatureFactory; 43 44 public class ContextualCardsFragment extends InstrumentedFragment implements 45 FocusRecyclerView.FocusListener { 46 47 private static final String TAG = "ContextualCardsFragment"; 48 private static final boolean DEBUG = Build.IS_DEBUGGABLE; 49 50 @VisibleForTesting 51 static boolean sRestartLoaderNeeded; 52 53 @VisibleForTesting 54 BroadcastReceiver mKeyEventReceiver; 55 @VisibleForTesting 56 BroadcastReceiver mScreenOffReceiver; 57 58 private FocusRecyclerView mCardsContainer; 59 private GridLayoutManager mLayoutManager; 60 private ContextualCardsAdapter mContextualCardsAdapter; 61 private ContextualCardManager mContextualCardManager; 62 private ItemTouchHelper mItemTouchHelper; 63 64 @Override onCreate(Bundle savedInstanceState)65 public void onCreate(Bundle savedInstanceState) { 66 super.onCreate(savedInstanceState); 67 final Context context = getContext(); 68 if (savedInstanceState == null) { 69 FeatureFactory.getFeatureFactory().getSlicesFeatureProvider().newUiSession(); 70 BluetoothUpdateWorker.initLocalBtManager(getContext()); 71 } 72 mContextualCardManager = new ContextualCardManager(context, getSettingsLifecycle(), 73 savedInstanceState); 74 mKeyEventReceiver = new KeyEventReceiver(); 75 } 76 77 @Override onStart()78 public void onStart() { 79 super.onStart(); 80 registerScreenOffReceiver(); 81 registerKeyEventReceiver(); 82 mContextualCardManager.loadContextualCards(LoaderManager.getInstance(this), 83 sRestartLoaderNeeded); 84 sRestartLoaderNeeded = false; 85 } 86 87 @Override onStop()88 public void onStop() { 89 unregisterKeyEventReceiver(); 90 super.onStop(); 91 } 92 93 @Override onDestroy()94 public void onDestroy() { 95 unregisterScreenOffReceiver(); 96 super.onDestroy(); 97 } 98 99 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)100 public View onCreateView(LayoutInflater inflater, ViewGroup container, 101 Bundle savedInstanceState) { 102 final Context context = getContext(); 103 final View rootView = inflater.inflate(R.layout.settings_homepage, container, false); 104 mCardsContainer = rootView.findViewById(R.id.card_container); 105 mLayoutManager = new GridLayoutManager(getActivity(), SPAN_COUNT, 106 GridLayoutManager.VERTICAL, false /* reverseLayout */); 107 mCardsContainer.setLayoutManager(mLayoutManager); 108 mContextualCardsAdapter = new ContextualCardsAdapter(context, this /* lifecycleOwner */, 109 mContextualCardManager); 110 mCardsContainer.setItemAnimator(null); 111 mCardsContainer.setAdapter(mContextualCardsAdapter); 112 mContextualCardManager.setListener(mContextualCardsAdapter); 113 mCardsContainer.setListener(this); 114 mItemTouchHelper = new ItemTouchHelper(new SwipeDismissalDelegate(mContextualCardsAdapter)); 115 mItemTouchHelper.attachToRecyclerView(mCardsContainer); 116 117 return rootView; 118 } 119 120 @Override onWindowFocusChanged(boolean hasWindowFocus)121 public void onWindowFocusChanged(boolean hasWindowFocus) { 122 mContextualCardManager.onWindowFocusChanged(hasWindowFocus); 123 } 124 125 @Override getMetricsCategory()126 public int getMetricsCategory() { 127 return SettingsEnums.SETTINGS_HOMEPAGE; 128 } 129 registerKeyEventReceiver()130 private void registerKeyEventReceiver() { 131 getActivity().registerReceiver(mKeyEventReceiver, 132 new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), Context.RECEIVER_EXPORTED); 133 } 134 unregisterKeyEventReceiver()135 private void unregisterKeyEventReceiver() { 136 getActivity().unregisterReceiver(mKeyEventReceiver); 137 } 138 registerScreenOffReceiver()139 private void registerScreenOffReceiver() { 140 if (mScreenOffReceiver == null) { 141 mScreenOffReceiver = new ScreenOffReceiver(); 142 getActivity().registerReceiver(mScreenOffReceiver, 143 new IntentFilter(Intent.ACTION_SCREEN_OFF)); 144 } 145 } 146 unregisterScreenOffReceiver()147 private void unregisterScreenOffReceiver() { 148 if (mScreenOffReceiver != null) { 149 getActivity().unregisterReceiver(mScreenOffReceiver); 150 mScreenOffReceiver = null; 151 } 152 } 153 resetSession(Context context)154 private void resetSession(Context context) { 155 sRestartLoaderNeeded = true; 156 unregisterScreenOffReceiver(); 157 FeatureFactory.getFeatureFactory().getSlicesFeatureProvider().newUiSession(); 158 } 159 160 /** 161 * Receiver for updating UI session when home key or recent app key is pressed. 162 */ 163 @VisibleForTesting 164 class KeyEventReceiver extends BroadcastReceiver { 165 166 private static final String KEY_REASON = "reason"; 167 private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; 168 private static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; 169 170 @Override onReceive(Context context, Intent intent)171 public void onReceive(Context context, Intent intent) { 172 if (intent == null || !Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { 173 return; 174 } 175 176 final String reason = intent.getStringExtra(KEY_REASON); 177 if (!SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason) 178 && !SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)) { 179 return; 180 } 181 182 if (DEBUG) { 183 Log.d(TAG, "key pressed = " + reason); 184 } 185 resetSession(context); 186 } 187 } 188 189 /** 190 * Receiver for updating UI session when screen is turned off. 191 */ 192 @VisibleForTesting 193 class ScreenOffReceiver extends BroadcastReceiver { 194 195 @Override onReceive(Context context, Intent intent)196 public void onReceive(Context context, Intent intent) { 197 if (intent == null || !Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { 198 return; 199 } 200 201 if (DEBUG) { 202 Log.d(TAG, "screen off"); 203 } 204 resetSession(context); 205 } 206 } 207 } 208