1 /* 2 * Copyright 2023 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 androidx.camera.integration.core; 18 19 import static androidx.camera.integration.core.CameraXService.ACTION_BIND_USE_CASES; 20 import static androidx.camera.integration.core.CameraXService.EXTRA_IMAGE_ANALYSIS_ENABLED; 21 import static androidx.camera.integration.core.CameraXService.EXTRA_IMAGE_CAPTURE_ENABLED; 22 import static androidx.camera.integration.core.CameraXService.EXTRA_VIDEO_CAPTURE_ENABLED; 23 24 import android.Manifest; 25 import android.content.Intent; 26 import android.content.pm.PackageManager; 27 import android.os.Build; 28 import android.os.Bundle; 29 import android.widget.Toast; 30 import android.widget.ToggleButton; 31 32 import androidx.activity.result.ActivityResultLauncher; 33 import androidx.activity.result.contract.ActivityResultContracts; 34 import androidx.appcompat.app.AppCompatActivity; 35 import androidx.core.content.ContextCompat; 36 37 import org.jspecify.annotations.NonNull; 38 import org.jspecify.annotations.Nullable; 39 40 import java.util.ArrayList; 41 import java.util.List; 42 import java.util.Objects; 43 44 /** 45 * An activity to configure CameraXService. 46 * 47 * <p>It will: 48 * <ul> 49 * <li>Grant runtime permissions.</li> 50 * <li>Start the CameraXService automatically.</li> 51 * <li>Control which use cases are bound to the CameraXService lifecycle.</li> 52 * </ul> 53 */ 54 public class ServiceSettingActivity extends AppCompatActivity { 55 56 private static final String[] REQUIRED_PERMISSIONS; 57 58 static { 59 List<String> permissions = new ArrayList<>(); 60 permissions.add(android.Manifest.permission.CAMERA); 61 permissions.add(android.Manifest.permission.RECORD_AUDIO); 62 // From Android T, skips the permission check of WRITE_EXTERNAL_STORAGE since it won't be 63 // granted any more. 64 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { 65 permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); 66 } else { 67 // From Android T, POST_NOTIFICATIONS is required for foreground service to post 68 // notification. 69 permissions.add(Manifest.permission.POST_NOTIFICATIONS); 70 } 71 REQUIRED_PERMISSIONS = permissions.toArray(new String[0]); 72 } 73 74 private ToggleButton mButtonVideo; 75 private ToggleButton mButtonImage; 76 private ToggleButton mButtonAnalysis; 77 78 @Override onCreate(@ullable Bundle savedInstanceState)79 protected void onCreate(@Nullable Bundle savedInstanceState) { 80 super.onCreate(savedInstanceState); 81 82 setContentView(R.layout.activity_service_setting); 83 mButtonVideo = findViewById(R.id.video_toggle); 84 mButtonImage = findViewById(R.id.image_toggle); 85 mButtonAnalysis = findViewById(R.id.analysis_toggle); 86 87 mButtonVideo.setOnClickListener(v -> bindUseCases()); 88 mButtonImage.setOnClickListener(v -> bindUseCases()); 89 mButtonAnalysis.setOnClickListener(v -> bindUseCases()); 90 91 grantPermissions(); 92 } 93 onPermissionGranted()94 private void onPermissionGranted() { 95 launchService(); 96 bindUseCases(); 97 } 98 launchService()99 private void launchService() { 100 Intent intent = createServiceIntent(null); 101 ContextCompat.startForegroundService(this, intent); 102 } 103 bindUseCases()104 private void bindUseCases() { 105 Intent intent = createServiceIntent(ACTION_BIND_USE_CASES); 106 intent.putExtra(EXTRA_VIDEO_CAPTURE_ENABLED, mButtonVideo.isChecked()); 107 intent.putExtra(EXTRA_IMAGE_CAPTURE_ENABLED, mButtonImage.isChecked()); 108 intent.putExtra(EXTRA_IMAGE_ANALYSIS_ENABLED, mButtonAnalysis.isChecked()); 109 ContextCompat.startForegroundService(this, intent); 110 } 111 createServiceIntent(@ullable String action)112 private @NonNull Intent createServiceIntent(@Nullable String action) { 113 Intent intent = new Intent(this, CameraXService.class); 114 intent.setAction(action); 115 return intent; 116 } 117 isPermissionMissing()118 private boolean isPermissionMissing() { 119 for (String permission : REQUIRED_PERMISSIONS) { 120 if (ContextCompat.checkSelfPermission(this, permission) 121 != PackageManager.PERMISSION_GRANTED) { 122 return true; 123 } 124 } 125 return false; 126 } 127 grantPermissions()128 private void grantPermissions() { 129 if (isPermissionMissing()) { 130 ActivityResultLauncher<String[]> permissionLauncher = registerForActivityResult( 131 new ActivityResultContracts.RequestMultiplePermissions(), 132 result -> { 133 for (String permission : REQUIRED_PERMISSIONS) { 134 if (!Objects.requireNonNull(result.get(permission))) { 135 Toast.makeText(this, "Camera permission denied.", 136 Toast.LENGTH_SHORT).show(); 137 finish(); 138 return; 139 } 140 } 141 onPermissionGranted(); 142 }); 143 144 permissionLauncher.launch(REQUIRED_PERMISSIONS); 145 } else { 146 onPermissionGranted(); 147 } 148 } 149 } 150