1 /* 2 * Copyright (C) 2021 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.nearby.halfsheet; 18 19 import static android.Manifest.permission.ACCESS_FINE_LOCATION; 20 21 import static com.android.nearby.halfsheet.constants.Constant.ACTION_FAST_PAIR_HALF_SHEET_BAN_STATE_RESET; 22 import static com.android.nearby.halfsheet.constants.Constant.ACTION_FAST_PAIR_HALF_SHEET_CANCEL; 23 import static com.android.nearby.halfsheet.constants.Constant.ACTION_HALF_SHEET_FOREGROUND_STATE; 24 import static com.android.nearby.halfsheet.constants.Constant.DEVICE_PAIRING_FRAGMENT_TYPE; 25 import static com.android.nearby.halfsheet.constants.Constant.EXTRA_HALF_SHEET_FOREGROUND; 26 import static com.android.nearby.halfsheet.constants.Constant.EXTRA_HALF_SHEET_INFO; 27 import static com.android.nearby.halfsheet.constants.Constant.EXTRA_HALF_SHEET_IS_RETROACTIVE; 28 import static com.android.nearby.halfsheet.constants.Constant.EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR; 29 import static com.android.nearby.halfsheet.constants.Constant.EXTRA_HALF_SHEET_TYPE; 30 import static com.android.nearby.halfsheet.constants.Constant.TAG; 31 import static com.android.nearby.halfsheet.constants.FastPairConstants.EXTRA_MODEL_ID; 32 import static com.android.nearby.halfsheet.constants.UserActionHandlerBase.EXTRA_MAC_ADDRESS; 33 import static com.android.nearby.halfsheet.fragment.DevicePairingFragment.APP_LAUNCH_FRAGMENT_TYPE; 34 35 import android.content.Intent; 36 import android.os.Bundle; 37 import android.util.Log; 38 import android.widget.TextView; 39 40 import androidx.annotation.NonNull; 41 import androidx.annotation.Nullable; 42 import androidx.fragment.app.FragmentActivity; 43 44 import com.android.nearby.halfsheet.fragment.DevicePairingFragment; 45 import com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment; 46 import com.android.nearby.halfsheet.utils.BroadcastUtils; 47 48 import com.google.protobuf.InvalidProtocolBufferException; 49 50 import java.util.Locale; 51 52 import service.proto.Cache; 53 54 /** 55 * A class show Fast Pair related information in Half sheet format. 56 */ 57 public class HalfSheetActivity extends FragmentActivity { 58 @Nullable 59 private HalfSheetModuleFragment mHalfSheetModuleFragment; 60 @Nullable 61 private Cache.ScanFastPairStoreItem mScanFastPairStoreItem; 62 63 @Override onCreate(@ullable Bundle savedInstanceState)64 protected void onCreate(@Nullable Bundle savedInstanceState) { 65 super.onCreate(savedInstanceState); 66 byte[] infoArray = getIntent().getByteArrayExtra(EXTRA_HALF_SHEET_INFO); 67 String fragmentType = getIntent().getStringExtra(EXTRA_HALF_SHEET_TYPE); 68 if (infoArray == null || fragmentType == null) { 69 Log.d( 70 "HalfSheetActivity", 71 "exit flag off or do not have enough half sheet information."); 72 finish(); 73 return; 74 } 75 76 switch (fragmentType) { 77 case DEVICE_PAIRING_FRAGMENT_TYPE: 78 mHalfSheetModuleFragment = DevicePairingFragment.newInstance(getIntent(), 79 savedInstanceState); 80 if (mHalfSheetModuleFragment == null) { 81 Log.d(TAG, "device pairing fragment has error."); 82 finish(); 83 return; 84 } 85 break; 86 case APP_LAUNCH_FRAGMENT_TYPE: 87 // currentFragment = AppLaunchFragment.newInstance(getIntent()); 88 if (mHalfSheetModuleFragment == null) { 89 Log.v(TAG, "app launch fragment has error."); 90 finish(); 91 return; 92 } 93 break; 94 default: 95 Log.w(TAG, "there is no valid type for half sheet"); 96 finish(); 97 return; 98 } 99 if (mHalfSheetModuleFragment != null) { 100 getSupportFragmentManager() 101 .beginTransaction() 102 .replace(R.id.fragment_container, mHalfSheetModuleFragment) 103 .commit(); 104 } 105 setContentView(R.layout.fast_pair_half_sheet); 106 107 // If the user taps on the background, then close the activity. 108 // Unless they tap on the card itself, then ignore the tap. 109 findViewById(R.id.background).setOnClickListener(v -> onCancelClicked()); 110 findViewById(R.id.card) 111 .setOnClickListener( 112 v -> Log.v(TAG, "card view is clicked noop")); 113 try { 114 mScanFastPairStoreItem = 115 Cache.ScanFastPairStoreItem.parseFrom(infoArray); 116 } catch (InvalidProtocolBufferException e) { 117 Log.w( 118 TAG, "error happens when pass info to half sheet"); 119 } 120 } 121 122 @Override onStart()123 protected void onStart() { 124 super.onStart(); 125 BroadcastUtils.sendBroadcast( 126 this, 127 new Intent(ACTION_HALF_SHEET_FOREGROUND_STATE) 128 .putExtra(EXTRA_HALF_SHEET_FOREGROUND, true)); 129 } 130 131 @Override onSaveInstanceState(@onNull Bundle savedInstanceState)132 protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) { 133 super.onSaveInstanceState(savedInstanceState); 134 if (mHalfSheetModuleFragment != null) { 135 mHalfSheetModuleFragment.onSaveInstanceState(savedInstanceState); 136 } 137 } 138 139 @Override onBackPressed()140 public void onBackPressed() { 141 super.onBackPressed(); 142 sendHalfSheetCancelBroadcast(); 143 } 144 145 @Override onUserLeaveHint()146 protected void onUserLeaveHint() { 147 super.onUserLeaveHint(); 148 sendHalfSheetCancelBroadcast(); 149 } 150 151 @Override onNewIntent(Intent intent)152 protected void onNewIntent(Intent intent) { 153 super.onNewIntent(intent); 154 String fragmentType = getIntent().getStringExtra(EXTRA_HALF_SHEET_TYPE); 155 if (fragmentType == null) { 156 return; 157 } 158 if (fragmentType.equals(DEVICE_PAIRING_FRAGMENT_TYPE) 159 && intent.getExtras() != null 160 && intent.getByteArrayExtra(EXTRA_HALF_SHEET_INFO) != null) { 161 try { 162 Cache.ScanFastPairStoreItem testScanFastPairStoreItem = 163 Cache.ScanFastPairStoreItem.parseFrom( 164 intent.getByteArrayExtra(EXTRA_HALF_SHEET_INFO)); 165 if (mScanFastPairStoreItem != null 166 && !testScanFastPairStoreItem.getAddress().equals( 167 mScanFastPairStoreItem.getAddress()) 168 && testScanFastPairStoreItem.getModelId().equals( 169 mScanFastPairStoreItem.getModelId())) { 170 Log.d(TAG, "possible factory reset happens"); 171 halfSheetStateChange(); 172 } 173 } catch (InvalidProtocolBufferException | NullPointerException e) { 174 Log.w(TAG, "error happens when pass info to half sheet"); 175 } 176 } 177 } 178 179 /** This function should be called when user click empty area and cancel button. */ onCancelClicked()180 public void onCancelClicked() { 181 Log.d(TAG, "Cancels the half sheet and paring."); 182 sendHalfSheetCancelBroadcast(); 183 finish(); 184 } 185 186 /** Changes the half sheet foreground state to false. */ halfSheetStateChange()187 public void halfSheetStateChange() { 188 BroadcastUtils.sendBroadcast( 189 this, 190 new Intent(ACTION_HALF_SHEET_FOREGROUND_STATE) 191 .putExtra(EXTRA_HALF_SHEET_FOREGROUND, false)); 192 finish(); 193 } 194 195 196 /** 197 * Changes the half sheet ban state to active. 198 * Sometimes users leave half sheet to go to fast pair info page, 199 * we do not want the behavior to be counted as dismiss. 200 */ sendBanStateResetBroadcast()201 public void sendBanStateResetBroadcast() { 202 if (mScanFastPairStoreItem == null) { 203 return; 204 } 205 BroadcastUtils.sendBroadcast( 206 this, 207 new Intent(ACTION_FAST_PAIR_HALF_SHEET_BAN_STATE_RESET) 208 .putExtra(EXTRA_MODEL_ID, mScanFastPairStoreItem.getModelId() 209 .toLowerCase(Locale.ROOT))); 210 } 211 sendHalfSheetCancelBroadcast()212 private void sendHalfSheetCancelBroadcast() { 213 BroadcastUtils.sendBroadcast( 214 this, 215 new Intent(ACTION_HALF_SHEET_FOREGROUND_STATE) 216 .putExtra(EXTRA_HALF_SHEET_FOREGROUND, false)); 217 if (mScanFastPairStoreItem == null) { 218 return; 219 } 220 BroadcastUtils.sendBroadcast( 221 this, 222 new Intent(ACTION_FAST_PAIR_HALF_SHEET_CANCEL) 223 .putExtra(EXTRA_MODEL_ID, 224 mScanFastPairStoreItem.getModelId().toLowerCase(Locale.ROOT)) 225 .putExtra(EXTRA_HALF_SHEET_TYPE, 226 getIntent().getStringExtra(EXTRA_HALF_SHEET_TYPE)) 227 .putExtra( 228 EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR, 229 getIntent().getBooleanExtra(EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR, 230 false)) 231 .putExtra( 232 EXTRA_HALF_SHEET_IS_RETROACTIVE, 233 getIntent().getBooleanExtra(EXTRA_HALF_SHEET_IS_RETROACTIVE, 234 false)) 235 .putExtra(EXTRA_MAC_ADDRESS, mScanFastPairStoreItem.getAddress()), 236 ACCESS_FINE_LOCATION); 237 } 238 239 @Override setTitle(CharSequence title)240 public void setTitle(CharSequence title) { 241 super.setTitle(title); 242 TextView toolbarTitle = findViewById(R.id.toolbar_title); 243 toolbarTitle.setText(title); 244 } 245 } 246