@@ -27,115 +27,120 @@ import java.io.FileOutputStream
2727
2828class MainActivity : AppCompatActivity () {
2929
30- // --- 1. CONTACT PICKER LAUNCHER ---
30+ // --- 1. CROPPER LAUNCHER ---
31+ // Receives the FINAL cropped image from your custom CropActivity
32+ private val cropActivityLauncher = registerForActivityResult(ActivityResultContracts .StartActivityForResult ()) { result ->
33+ if (result.resultCode == RESULT_OK ) {
34+ val croppedUri = result.data?.data
35+ if (croppedUri != null ) {
36+ // Show it
37+ findViewById< android.widget.ImageView > (R .id.ivPreview).setImageURI(croppedUri)
38+ // Save it locally
39+ saveImageToInternalStorage(croppedUri)
40+ }
41+ }
42+ }
43+
44+ // --- 2. CLASSIC GALLERY LAUNCHER (Re-implemented) ---
45+ // Receives the raw image from the Gallery, then immediately sends it to CropActivity
46+ private val pickImageLauncher = registerForActivityResult(ActivityResultContracts .StartActivityForResult ()) { result ->
47+ if (result.resultCode == RESULT_OK ) {
48+ val originalUri = result.data?.data
49+ if (originalUri != null ) {
50+ // Immediately launch CropActivity with this image
51+ val intent = Intent (this , CropActivity ::class .java)
52+ intent.data = originalUri
53+ intent.flags = Intent .FLAG_GRANT_READ_URI_PERMISSION
54+ cropActivityLauncher.launch(intent)
55+ }
56+ }
57+ }
58+
59+ // --- 3. CONTACT PICKER LAUNCHER ---
3160 private val pickContactLauncher = registerForActivityResult(ActivityResultContracts .StartActivityForResult ()) { result ->
3261 if (result.resultCode == RESULT_OK ) {
3362 val contactUri = result.data?.data ? : return @registerForActivityResult
3463 populateContactDetails(contactUri)
3564 }
3665 }
3766
38- // --- 2. PERMISSION LAUNCHER ---
3967 private val requestContactPermissionLauncher = registerForActivityResult(ActivityResultContracts .RequestPermission ()) { isGranted ->
4068 if (isGranted) {
4169 launchContactPicker()
4270 } else {
43- Toast .makeText(this , " Permission denied. Cannot pick contact." , Toast .LENGTH_SHORT ).show()
44- }
45- }
46-
47- private val pickImageLauncher = registerForActivityResult(ActivityResultContracts .StartActivityForResult ()) { result ->
48- if (result.resultCode == RESULT_OK ) {
49- val uri = result.data?.data
50- if (uri != null ) {
51- findViewById< android.widget.ImageView > (R .id.ivPreview).setImageURI(uri)
52- saveImageToInternalStorage(uri)
53- }
71+ Toast .makeText(this , " Permission denied." , Toast .LENGTH_SHORT ).show()
5472 }
5573 }
5674
5775 override fun onCreate (savedInstanceState : Bundle ? ) {
5876 super .onCreate(savedInstanceState)
5977 setContentView(R .layout.activity_main)
6078
61- checkPermissions() // Standard permissions (Notif, Overlay, Alarm)
79+ checkPermissions()
6280
63- // Setup UI
6481 val ivPreview = findViewById< android.widget.ImageView > (R .id.ivPreview)
65- val btnPickContact = findViewById<ImageButton >(R .id.btnPickContact) // <--- NEW BUTTON
82+ val btnPickContact = findViewById<ImageButton >(R .id.btnPickContact)
6683 val btnSchedule = findViewById<Button >(R .id.btnSchedule)
6784 val npDelay = findViewById<NumberPicker >(R .id.npDelay)
6885
69- // Setup Spinner
7086 npDelay.minValue = 0
7187 npDelay.maxValue = 120
7288 npDelay.value = 10
7389 npDelay.wrapSelectorWheel = false
7490
75- // 1. IMAGE CLICK (Manual Photo)
91+ // --- 4. CLICK LISTENER: Use Standard Gallery Intent ---
7692 ivPreview.setOnClickListener {
93+ // ACTION_PICK is the "Classic" single-select intent you wanted
7794 val intent = Intent (Intent .ACTION_PICK , MediaStore .Images .Media .EXTERNAL_CONTENT_URI )
7895 pickImageLauncher.launch(intent)
7996 }
8097
81- // 2. CONTACT BUTTON CLICK
8298 btnPickContact.setOnClickListener {
83- // Check if we have permission. If not, ask. If yes, open picker.
8499 if (ContextCompat .checkSelfPermission(this , android.Manifest .permission.READ_CONTACTS ) == PackageManager .PERMISSION_GRANTED ) {
85100 launchContactPicker()
86101 } else {
87102 requestContactPermissionLauncher.launch(android.Manifest .permission.READ_CONTACTS )
88103 }
89104 }
90105
91- // 3. SCHEDULE CLICK
92106 btnSchedule.setOnClickListener {
93107 val name = findViewById<EditText >(R .id.etCallerName).text.toString()
94108 val phone = findViewById<EditText >(R .id.etPhoneNumber).text.toString()
95109 val delaySeconds = npDelay.value.toLong()
96110 scheduleCall(name, phone, delaySeconds)
97111 }
98112
99- // Cleanup old image on start
113+ // Reset image on fresh launch
100114 val file = File (filesDir, " custom_avatar.jpg" )
101115 if (file.exists()) file.delete()
102116 }
103117
104118 private fun launchContactPicker () {
105- // We strictly want phones, so we pick from CommonDataKinds.Phone
106119 val intent = Intent (Intent .ACTION_PICK , ContactsContract .CommonDataKinds .Phone .CONTENT_URI )
107120 pickContactLauncher.launch(intent)
108121 }
109122
110123 private fun populateContactDetails (uri : Uri ) {
111- // Query the content provider for the specific contact clicked
112124 val cursor: Cursor ? = contentResolver.query(uri, null , null , null , null )
113-
114125 cursor?.use {
115126 if (it.moveToFirst()) {
116- // 1. Get Name
117127 val nameIndex = it.getColumnIndex(ContactsContract .CommonDataKinds .Phone .DISPLAY_NAME )
118128 val name = if (nameIndex >= 0 ) it.getString(nameIndex) else " Unknown"
119-
120- // 2. Get Number
121129 val numberIndex = it.getColumnIndex(ContactsContract .CommonDataKinds .Phone .NUMBER )
122130 val number = if (numberIndex >= 0 ) it.getString(numberIndex) else " "
123-
124- // 3. Get Photo URI (Thumbnail or High Res)
125131 val photoUriIndex = it.getColumnIndex(ContactsContract .CommonDataKinds .Phone .PHOTO_URI )
126132 val photoUriString = if (photoUriIndex >= 0 ) it.getString(photoUriIndex) else null
127133
128- // --- UPDATE UI ---
129134 findViewById<EditText >(R .id.etCallerName).setText(name)
130135 findViewById<EditText >(R .id.etPhoneNumber).setText(number)
131136
132- // --- HANDLE PHOTO ---
133137 if (photoUriString != null ) {
134138 val photoUri = photoUriString.toUri()
139+ // If from contacts, also let them crop it if they want, or just save it.
140+ // For now, let's just save it directly to keep it simple.
135141 findViewById< android.widget.ImageView > (R .id.ivPreview).setImageURI(photoUri)
136- saveImageToInternalStorage(photoUri) // Save it for the fake call later
142+ saveImageToInternalStorage(photoUri)
137143 } else {
138- // Reset to default if contact has no photo
139144 findViewById< android.widget.ImageView > (R .id.ivPreview).setImageResource(R .drawable.ic_avatar_default)
140145 val file = File (filesDir, " custom_avatar.jpg" )
141146 if (file.exists()) file.delete()
@@ -158,7 +163,6 @@ class MainActivity : AppCompatActivity() {
158163 }
159164
160165 private fun checkPermissions () {
161- // ... (Keep your existing checks for Notif, Overlay, Alarm) ...
162166 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
163167 ActivityCompat .requestPermissions(this , arrayOf(android.Manifest .permission.POST_NOTIFICATIONS ), 101 )
164168 }
@@ -174,7 +178,6 @@ class MainActivity : AppCompatActivity() {
174178 }
175179
176180 private fun scheduleCall (name : String , phone : String , delaySeconds : Long ) {
177- // ... (Keep your existing schedule logic exactly as it was) ...
178181 if (delaySeconds == 0L ) {
179182 val intent = Intent (this , FakeCallActivity ::class .java).apply {
180183 putExtra(" caller_name" , name)
@@ -195,10 +198,16 @@ class MainActivity : AppCompatActivity() {
195198 putExtra(" caller_name" , name)
196199 putExtra(" caller_phone" , phone)
197200 }
198- val pendingIntent = PendingIntent .getBroadcast(this , 0 , intent, PendingIntent .FLAG_UPDATE_CURRENT or PendingIntent .FLAG_IMMUTABLE )
199-
200- alarmManager.setExactAndAllowWhileIdle(AlarmManager .RTC_WAKEUP , System .currentTimeMillis() + (delaySeconds * 1000 ), pendingIntent)
201- Toast .makeText(this , " Call scheduled in $delaySeconds second${if (delaySeconds != 1L ) " s" else " " } ." , Toast .LENGTH_LONG ).show()
201+ val pendingIntent = PendingIntent .getBroadcast(
202+ this , 0 , intent, PendingIntent .FLAG_UPDATE_CURRENT or PendingIntent .FLAG_IMMUTABLE
203+ )
204+
205+ alarmManager.setExactAndAllowWhileIdle(
206+ AlarmManager .RTC_WAKEUP ,
207+ System .currentTimeMillis() + (delaySeconds * 1000 ),
208+ pendingIntent
209+ )
210+ Toast .makeText(this , " Call scheduled in $delaySeconds second${if (delaySeconds != 1L ) " s" else " " } " , Toast .LENGTH_SHORT ).show()
202211
203212 }
204213}
0 commit comments