基本电话接听与拨打功能完成

This commit is contained in:
ZhanGSKen 2025-02-21 06:13:51 +08:00
parent 64a9712294
commit c590c96fa6
26 changed files with 1354 additions and 40 deletions

View File

@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Thu Feb 20 09:27:14 GMT 2025
#Thu Feb 20 22:09:07 GMT 2025
stageCount=0
libraryProject=winboll-shared
baseVersion=1.0
publishVersion=1.0.0
buildCount=99
buildCount=126
baseBetaVersion=1.0.1

View File

@ -9,6 +9,24 @@
<!-- 拨打电话 -->
<uses-permission android:name="android.permission.CALL_PHONE"/>
<!-- 读取手机状态和身份 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<!-- 修改系统设置 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<!-- 重新设置外拨电话的路径 -->
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<!-- 读取联系人 -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<!-- 修改您的通讯录 -->
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<!-- 此应用可显示在其他应用上方 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application
android:name=".App"
android:allowBackup="true"
@ -32,18 +50,44 @@
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<activity
android:name=".activities.CallActivity"
android:label="CallActivity"
android:launchMode="singleTask"
android:exported="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/studio_provider"/>
</activity>
</provider>
<activity
android:name=".phonecallui.PhoneCallActivity"
android:launchMode="singleInstance"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.DIAL"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="tel"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.DIAL"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<service
android:name="cc.winboll.studio.contacts.services.MainService"
@ -51,7 +95,37 @@
<service android:name="cc.winboll.studio.contacts.services.AssistantService"/>
<receiver android:name="cc.winboll.studio.contacts.receivers.MainReceiver">
<service
android:name=".phonecallui.PhoneCallService"
android:permission="android.permission.BIND_INCALL_SERVICE"
android:exported="false">
<meta-data
android:name="android.telecom.IN_CALL_SERVICE_UI"
android:value="true"/>
<intent-filter>
<action android:name="android.telecom.InCallService"/>
</intent-filter>
</service>
<service
android:name=".listenphonecall.CallListenerService"
android:enabled="true"
android:exported="false">
<intent-filter android:priority="1000">
<action android:name=".service.CallShowService"/>
</intent-filter>
</service>
<receiver android:name=".receivers.MainReceiver">
<intent-filter>
@ -91,8 +165,20 @@
</receiver>
<activity android:name="cc.winboll.studio.contacts.activities.CallActivity"/>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/studio_provider"/>
</provider>
<activity android:name="cc.winboll.studio.contacts.activities.SettingsActivity"/>
</application>
</manifest>
</manifest>

View File

@ -0,0 +1,59 @@
package cc.winboll.studio.contacts;
import android.app.Activity;
import java.util.ArrayList;
import java.util.List;
public class ActivityStack {
private static final ActivityStack INSTANCE = new ActivityStack();
private List<Activity> activities = new ArrayList<>();
public static ActivityStack getInstance() {
return INSTANCE;
}
public void addActivity(Activity activity) {
activities.add(activity);
}
public Activity getTopActivity() {
if (activities.isEmpty()) {
return null;
}
return activities.get(activities.size() - 1);
}
public void finishTopActivity() {
if (!activities.isEmpty()) {
activities.remove(activities.size() - 1).finish();
}
}
public void finishActivity(Activity activity) {
if (activity != null) {
activities.remove(activity);
activity.finish();
}
}
public void finishActivity(Class activityClass) {
for (Activity activity : activities) {
if (activity.getClass().equals(activityClass)) {
finishActivity(activity);
}
}
}
public void finishAllActivity() {
if (!activities.isEmpty()) {
for (Activity activity : activities) {
activity.finish();
activities.remove(activity);
}
}
}
}

View File

@ -1,30 +1,52 @@
package cc.winboll.studio.contacts;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.role.RoleManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.telecom.TelecomManager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.CheckBox;
import com.google.android.material.tabs.TabLayout;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.viewpager.widget.ViewPager;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.activities.CallActivity;
import cc.winboll.studio.contacts.adapters.MyPagerAdapter;
import cc.winboll.studio.contacts.beans.MainServiceBean;
import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.libapputils.app.IWinBollActivity;
import cc.winboll.studio.libapputils.app.WinBollActivityManager;
import cc.winboll.studio.libapputils.bean.APPInfo;
import cc.winboll.studio.libapputils.view.YesNoAlertDialog;
import android.widget.ImageView;
import android.view.View;
import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
import com.google.android.material.tabs.TabLayout;
import java.lang.reflect.Field;
import java.util.ArrayList;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import java.util.List;
import cc.winboll.studio.contacts.activities.CallActivity;
import android.content.DialogInterface;
import cc.winboll.studio.contacts.activities.SettingsActivity;
final public class MainActivity extends AppCompatActivity implements IWinBollActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
@ -48,6 +70,8 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
LinearLayout linearLayout;//下标所在在LinearLayout布局里
int currentPoint = 0;//当前被选中中页面的下标
private static final int DIALER_REQUEST_CODE = 1;
@Override
public AppCompatActivity getActivity() {
return this;
@ -90,7 +114,7 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
getSupportActionBar().setSubtitle(getTag());
initData();
initView();//调用初始化视图方法
initView();
//initPoint();//调用初始化导航原点的方法
viewPager.addOnPageChangeListener(this);//滑动事件
@ -116,9 +140,9 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
// }
// }
// });
MainService.startMainService(MainActivity.this);
}
//初始化view即显示的图片
void initView() {
viewPager = findViewById(R.id.activitymainViewPager1);
@ -311,6 +335,10 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
Intent intent = new Intent(this, CallActivity.class);
startActivity(intent);
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);
} else if (item.getItemId() == R.id.item_settings) {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);
}
// } else
// if (item.getItemId() == R.id.item_exit) {
@ -320,20 +348,59 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
return super.onOptionsItemSelected(item);
}
@Override
protected void onResume() {
super.onResume();
}
/**
* Android M 及以上检查是否是系统默认电话应用
*/
public boolean isDefaultPhoneCallApp() {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE);
if (manger != null && manger.getDefaultDialerPackage() != null) {
return manger.getDefaultDialerPackage().equals(getPackageName());
}
}
return false;
}
public boolean isServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
if (manager == null) return false;
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (resultCode) {
case REQUEST_HOME_ACTIVITY : {
LogUtils.d(TAG, "REQUEST_HOME_ACTIVITY");
break;
}
case REQUEST_ABOUT_ACTIVITY : {
LogUtils.d(TAG, "REQUEST_ABOUT_ACTIVITY");
break;
}
default : {
super.onActivityResult(requestCode, resultCode, data);
}
// switch (resultCode) {
// case REQUEST_HOME_ACTIVITY : {
// LogUtils.d(TAG, "REQUEST_HOME_ACTIVITY");
// break;
// }
// case REQUEST_ABOUT_ACTIVITY : {
// LogUtils.d(TAG, "REQUEST_ABOUT_ACTIVITY");
// break;
// }
// default : {
// super.onActivityResult(requestCode, resultCode, data);
// }
// }
if (requestCode == DIALER_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用",
Toast.LENGTH_SHORT).show();
}
}
}
}

View File

@ -0,0 +1,35 @@
package cc.winboll.studio.contacts;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 21:14:52
* @Describe PhoneCallManager
*/
import android.telecom.Call;
import android.telecom.VideoProfile;
public class PhoneCallManager {
public static final String TAG = "PhoneCallManager";
public static Call call;
/**
* 接听电话
*/
public void answer() {
if (call != null) {
call.answer(VideoProfile.STATE_AUDIO_ONLY);
}
}
/**
* 断开电话包括来电时的拒接以及接听后的挂断
*/
public void disconnect() {
if (call != null) {
call.disconnect();
}
}
}

View File

@ -0,0 +1,40 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 20:18:26
*/
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.contacts.R;
public class DialerActivity extends AppCompatActivity {
public static final String TAG = "DialerActivity";
private EditText phoneNumberEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dialer);
phoneNumberEditText = findViewById(R.id.phone_number_edit_text);
Button dialButton = findViewById(R.id.dial_button);
dialButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String phoneNumber = phoneNumberEditText.getText().toString();
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber));
startActivity(intent);
}
});
}
}

View File

@ -0,0 +1,138 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/21 05:37:42
*/
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.contacts.R;
import com.hjq.toast.ToastUtils;
import java.lang.reflect.Field;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libappbase.IWinBollActivity;
import cc.winboll.studio.libappbase.bean.APPInfo;
public class SettingsActivity extends AppCompatActivity implements IWinBollActivity {
public static final String TAG = "SettingsActivity";
Toolbar mToolbar;
@Override
public APPInfo getAppInfo() {
return null;
}
@Override
public AppCompatActivity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
public Toolbar initToolBar() {
return findViewById(R.id.activitymainToolbar1);
}
@Override
public boolean isAddWinBollToolBar() {
return true;
}
@Override
public boolean isEnableDisplayHomeAsUp() {
return false;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
// 初始化工具栏
mToolbar = findViewById(R.id.activitymainToolbar1);
setSupportActionBar(mToolbar);
if (isEnableDisplayHomeAsUp()) {
// 显示后退按钮
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
getSupportActionBar().setSubtitle(getTag());
}
public void onDefaultPhone(View view) {
Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
startActivity(intent);
}
public void onCanDrawOverlays(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !Settings.canDrawOverlays(this)) {
// 请求 悬浮框 权限
askForDrawOverlay();
} else {
ToastUtils.show("悬浮窗已开启");
}
}
private void askForDrawOverlay() {
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setTitle("允许显示悬浮框")
.setMessage("为了使电话监听服务正常工作,请允许这项权限")
.setPositiveButton("去设置", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
openDrawOverlaySettings();
dialog.dismiss();
}
})
.setNegativeButton("稍后再说", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.create();
//noinspection ConstantConditions
alertDialog.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
alertDialog.show();
}
/**
* 跳转悬浮窗管理设置界面
*/
private void openDrawOverlaySettings() {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Android M 以上引导用户去系统设置中打开允许悬浮窗
// 使用反射是为了用尽可能少的代码保证在大部分机型上都可用
try {
Context context = this;
Class clazz = Settings.class;
Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION");
Intent intent = new Intent(field.get(null).toString());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse("package:" + context.getPackageName()));
context.startActivity(intent);
} catch (Exception e) {
Toast.makeText(this, "请在悬浮窗管理中打开权限", Toast.LENGTH_LONG).show();
}
}
}
}

View File

@ -0,0 +1,206 @@
package cc.winboll.studio.contacts.listenphonecall;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.IBinder;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import cc.winboll.studio.contacts.MainActivity;
import cc.winboll.studio.contacts.R;
public class CallListenerService extends Service {
private View phoneCallView;
private TextView tvCallNumber;
private Button btnOpenApp;
private WindowManager windowManager;
private WindowManager.LayoutParams params;
private PhoneStateListener phoneStateListener;
private TelephonyManager telephonyManager;
private String callNumber;
private boolean hasShown;
private boolean isCallingIn;
@Override
public void onCreate() {
super.onCreate();
initPhoneStateListener();
initPhoneCallView();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* 初始化来电状态监听器
*/
private void initPhoneStateListener() {
phoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
callNumber = incomingNumber;
switch (state) {
case TelephonyManager.CALL_STATE_IDLE: // 待机即无电话时挂断时触发
dismiss();
break;
case TelephonyManager.CALL_STATE_RINGING: // 响铃来电时触发
isCallingIn = true;
updateUI();
show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK: // 摘机接听或拨出电话时触发
updateUI();
show();
break;
default:
break;
}
}
};
// 设置来电监听器
telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
if (telephonyManager != null) {
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
}
private void initPhoneCallView() {
windowManager = (WindowManager) getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE);
int width = windowManager.getDefaultDisplay().getWidth();
int height = windowManager.getDefaultDisplay().getHeight();
params = new WindowManager.LayoutParams();
params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
params.width = width;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
// 设置图片格式效果为背景透明
params.format = PixelFormat.TRANSLUCENT;
// 设置 Window flag 为系统级弹框 | 覆盖表层
params.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_PHONE;
// 不可聚集不响应返回键| 全屏
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
// API 19 以上则还可以开启透明状态栏与导航栏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
params.flags = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
}
FrameLayout interceptorLayout = new FrameLayout(this) {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
return true;
}
}
return super.dispatchKeyEvent(event);
}
};
phoneCallView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.inflate(R.layout.view_phone_call, interceptorLayout);
tvCallNumber = phoneCallView.findViewById(R.id.tv_call_number);
btnOpenApp = phoneCallView.findViewById(R.id.btn_open_app);
btnOpenApp.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
CallListenerService.this.startActivity(intent);
}
});
}
/**
* 显示顶级弹框展示通话信息
*/
private void show() {
if (!hasShown) {
windowManager.addView(phoneCallView, params);
hasShown = true;
}
}
/**
* 取消显示
*/
private void dismiss() {
if (hasShown) {
windowManager.removeView(phoneCallView);
isCallingIn = false;
hasShown = false;
}
}
private void updateUI() {
tvCallNumber.setText(formatPhoneNumber(callNumber));
int callTypeDrawable = isCallingIn ? R.drawable.ic_phone_call_in : R.drawable.ic_phone_call_out;
tvCallNumber.setCompoundDrawablesWithIntrinsicBounds(null, null,
getResources().getDrawable(callTypeDrawable), null);
}
public static String formatPhoneNumber(String phoneNum) {
if (!TextUtils.isEmpty(phoneNum) && phoneNum.length() == 11) {
return phoneNum.substring(0, 3) + "-"
+ phoneNum.substring(3, 7) + "-"
+ phoneNum.substring(7);
}
return phoneNum;
}
@Override
public void onDestroy() {
super.onDestroy();
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
}
}

View File

@ -0,0 +1,166 @@
package cc.winboll.studio.contacts.phonecallui;
import static cc.winboll.studio.contacts.listenphonecall.CallListenerService.formatPhoneNumber;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.contacts.ActivityStack;
import cc.winboll.studio.contacts.R;
import java.util.Timer;
import java.util.TimerTask;
/**
* 提供接打电话的界面仅支持 Android M (6.0, API 23) 及以上的系统
*
* @author aJIEw
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public class PhoneCallActivity extends AppCompatActivity implements View.OnClickListener {
private TextView tvCallNumberLabel;
private TextView tvCallNumber;
private TextView tvPickUp;
private TextView tvCallingTime;
private TextView tvHangUp;
private PhoneCallManager phoneCallManager;
private PhoneCallService.CallType callType;
private String phoneNumber;
private Timer onGoingCallTimer;
private int callingTime;
public static void actionStart(Context context, String phoneNumber,
PhoneCallService.CallType callType) {
Intent intent = new Intent(context, PhoneCallActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_MIME_TYPES, callType);
intent.putExtra(Intent.EXTRA_PHONE_NUMBER, phoneNumber);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_phone_call);
ActivityStack.getInstance().addActivity(this);
initData();
initView();
}
private void initData() {
phoneCallManager = new PhoneCallManager(this);
onGoingCallTimer = new Timer();
if (getIntent() != null) {
phoneNumber = getIntent().getStringExtra(Intent.EXTRA_PHONE_NUMBER);
callType = (PhoneCallService.CallType) getIntent().getSerializableExtra(Intent.EXTRA_MIME_TYPES);
}
}
private void initView() {
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
getWindow().getDecorView().setSystemUiVisibility(uiOptions);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
tvCallNumberLabel = findViewById(R.id.tv_call_number_label);
tvCallNumber = findViewById(R.id.tv_call_number);
tvPickUp = findViewById(R.id.tv_phone_pick_up);
tvCallingTime = findViewById(R.id.tv_phone_calling_time);
tvHangUp = findViewById(R.id.tv_phone_hang_up);
tvCallNumber.setText(formatPhoneNumber(phoneNumber));
tvPickUp.setOnClickListener(this);
tvHangUp.setOnClickListener(this);
// 打进的电话
if (callType == PhoneCallService.CallType.CALL_IN) {
tvCallNumberLabel.setText("来电号码");
tvPickUp.setVisibility(View.VISIBLE);
}
// 打出的电话
else if (callType == PhoneCallService.CallType.CALL_OUT) {
tvCallNumberLabel.setText("呼叫号码");
tvPickUp.setVisibility(View.GONE);
phoneCallManager.openSpeaker();
}
showOnLockScreen();
}
public void showOnLockScreen() {
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.tv_phone_pick_up) {
phoneCallManager.answer();
tvPickUp.setVisibility(View.GONE);
tvCallingTime.setVisibility(View.VISIBLE);
onGoingCallTimer.schedule(new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@SuppressLint("SetTextI18n")
@Override
public void run() {
callingTime++;
tvCallingTime.setText("通话中:" + getCallingTime());
}
});
}
}, 0, 1000);
} else if (v.getId() == R.id.tv_phone_hang_up) {
phoneCallManager.disconnect();
stopTimer();
}
}
private String getCallingTime() {
int minute = callingTime / 60;
int second = callingTime % 60;
return (minute < 10 ? "0" + minute : minute) +
":" +
(second < 10 ? "0" + second : second);
}
private void stopTimer() {
if (onGoingCallTimer != null) {
onGoingCallTimer.cancel();
}
callingTime = 0;
}
@Override
protected void onDestroy() {
super.onDestroy();
phoneCallManager.destroy();
}
}

View File

@ -0,0 +1,63 @@
package cc.winboll.studio.contacts.phonecallui;
import android.content.Context;
import android.media.AudioManager;
import android.os.Build;
import android.telecom.Call;
import android.telecom.VideoProfile;
import androidx.annotation.RequiresApi;
@RequiresApi(api = Build.VERSION_CODES.M)
public class PhoneCallManager {
public static Call call;
private Context context;
private AudioManager audioManager;
public PhoneCallManager(Context context) {
this.context = context;
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
/**
* 接听电话
*/
public void answer() {
if (call != null) {
call.answer(VideoProfile.STATE_AUDIO_ONLY);
openSpeaker();
}
}
/**
* 断开电话包括来电时的拒接以及接听后的挂断
*/
public void disconnect() {
if (call != null) {
call.disconnect();
}
}
/**
* 打开免提
*/
public void openSpeaker() {
if (audioManager != null) {
audioManager.setMode(AudioManager.MODE_IN_CALL);
audioManager.setSpeakerphoneOn(true);
}
}
/**
* 销毁资源
*/
public void destroy() {
call = null;
context = null;
audioManager = null;
}
}

View File

@ -0,0 +1,76 @@
package cc.winboll.studio.contacts.phonecallui;
import android.os.Build;
import android.telecom.Call;
import android.telecom.InCallService;
import androidx.annotation.RequiresApi;
import cc.winboll.studio.contacts.ActivityStack;
/**
* 监听电话通信状态的服务实现该类的同时必须提供电话管理的 UI
*
* @author aJIEw
* @see PhoneCallActivity
* @see android.telecom.InCallService
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public class PhoneCallService extends InCallService {
private final Call.Callback callback = new Call.Callback() {
@Override
public void onStateChanged(Call call, int state) {
super.onStateChanged(call, state);
switch (state) {
case Call.STATE_ACTIVE: {
break;
}
case Call.STATE_DISCONNECTED: {
ActivityStack.getInstance().finishActivity(PhoneCallActivity.class);
break;
}
}
}
};
@Override
public void onCallAdded(Call call) {
super.onCallAdded(call);
call.registerCallback(callback);
PhoneCallManager.call = call;
CallType callType = null;
if (call.getState() == Call.STATE_RINGING) {
callType = CallType.CALL_IN;
} else if (call.getState() == Call.STATE_CONNECTING) {
callType = CallType.CALL_OUT;
}
if (callType != null) {
Call.Details details = call.getDetails();
String phoneNumber = details.getHandle().getSchemeSpecificPart();
PhoneCallActivity.actionStart(this, phoneNumber, callType);
}
}
@Override
public void onCallRemoved(Call call) {
super.onCallRemoved(call);
call.unregisterCallback(callback);
PhoneCallManager.call = null;
}
public enum CallType {
CALL_IN,
CALL_OUT,
}
}

View File

@ -32,8 +32,9 @@ public class MainReceiver extends BroadcastReceiver {
String szAction = intent.getAction();
if (szAction.equals(ACTION_BOOT_COMPLETED)) {
ToastUtils.show("ACTION_BOOT_COMPLETED");
MainService.startMainService(context);
} else {
ToastUtils.show("szAction");
ToastUtils.show(szAction);
}
}
@ -42,7 +43,7 @@ public class MainReceiver extends BroadcastReceiver {
public void registerAction(Context context) {
IntentFilter filter=new IntentFilter();
filter.addAction(ACTION_BOOT_COMPLETED);
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
//filter.addAction(Intent.ACTION_BATTERY_CHANGED);
context.registerReceiver(this, filter);
}
}

View File

@ -0,0 +1,34 @@
package cc.winboll.studio.contacts.receivers;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 17:54:55
* @Describe PhoneCallReceiver
*/
//import android.content.BroadcastReceiver;
//import android.content.Context;
//import android.content.Intent;
//import android.telephony.TelephonyManager;
//import android.util.Log;
//import cc.winboll.studio.contacts.services.PhoneCallService;
//
//public class PhoneReceiver extends BroadcastReceiver {
//
// public static final String TAG = "PhoneReceiver";
//
// @Override
// public void onReceive(Context context, Intent intent) {
// if (intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
// String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
// if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
// String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
// Log.d(TAG, "Incoming call from: " + phoneNumber);
//
// // 启动服务来处理电话接听
// Intent serviceIntent = new Intent(context, PhoneCallService.class);
// serviceIntent.putExtra("phoneNumber", phoneNumber);
// context.startService(serviceIntent);
// }
// }
// }
//}

View File

@ -11,6 +11,7 @@ package cc.winboll.studio.contacts.services;
* https://blog.csdn.net/cyp331203/article/details/38920491
*/
import android.app.Service;
import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@ -97,7 +98,8 @@ public class MainService extends Service {
mMainReceiver = new MainReceiver(this);
mMainReceiver.registerAction(this);
}
startPhoneCallListener();
MainServiceThread.getInstance(this, mMainServiceHandler).start();
@ -125,6 +127,11 @@ public class MainService extends Service {
// LogUtils.d(TAG, "startService(intent)");
// bindService(new Intent(this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
}
void startPhoneCallListener() {
Intent callListener = new Intent(this, CallListenerService.class);
startService(callListener);
}
@Override
public void onDestroy() {

View File

@ -0,0 +1,75 @@
package cc.winboll.studio.contacts.services;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 19:58:02
* @Describe MyPhoneCallService
*/
import android.telecom.Call;
import android.telecom.InCallService;
import cc.winboll.studio.contacts.PhoneCallManager;
public class PhoneCallService extends InCallService {
public static final String TAG = "PhoneCallService";
private Call.Callback callback = new Call.Callback() {
@Override
public void onStateChanged(Call call, int state) {
super.onStateChanged(call, state);
switch (state) {
case Call.STATE_ACTIVE: {
break; // 通话中
}
case Call.STATE_DISCONNECTED: {
break; // 通话结束
}
}
}
};
// @Override
// public void onCallAdded(Call call) {
// super.onCallAdded(call);
//
// call.registerCallback(callback);
// }
//
// @Override
// public void onCallRemoved(Call call) {
// super.onCallRemoved(call);
//
// call.unregisterCallback(callback);
// }
@Override
public void onCallAdded(Call call) {
super.onCallAdded(call);
call.registerCallback(callback);
PhoneCallManager.call = call; // 传入call
// CallType callType = null;
//
// if (call.getState() == Call.STATE_RINGING) {
// callType = CallType.CALL_IN;
// } else if (call.getState() == Call.STATE_CONNECTING) {
// callType = CallType.CALL_OUT;
// }
//
// if (callType != null) {
// Call.Details details = call.getDetails();
// String phoneNumber = details.getHandle().toString().substring(4)
// .replaceAll("%20", ""); // 去除拨出电话中的空格
// PhoneCallActivity.actionStart(this, phoneNumber, callType);
// }
}
@Override
public void onCallRemoved(Call call) {
super.onCallRemoved(call);
call.unregisterCallback(callback);
PhoneCallManager.call = null;
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:viewportHeight="1024"
android:viewportWidth="1024">
<path
android:fillColor="#fff"
android:pathData="M847.53,307.03L597.5,307.03c-8.54,0.17 -13.66,-11.26 -13.66,-19.97 0.17,-8.7 5.12,-14.17 13.66,-14.17h250.02c30.21,-0.34 31.41,34.14 0,34.14zM736.6,428.71c-6.14,6.32 -16.05,6.32 -22.02,0.17l-128.34,-130.56c-5.98,-6.14 -5.8,-16.22 0.17,-22.53l122.37,-124.76c6.14,-6.31 16.05,-6.31 22.02,-0.34 5.98,6.14 5.81,16.22 -0.34,22.36L623.1,289.62l113.5,116.22a16.33,16.33 0,0 1,0 22.87zM833.02,717.99c-18.78,-12.63 -122.37,-64.85 -141.99,-77.14 -2.05,0 -7.34,0.68 -14.85,4.43 -8.02,4.1 -31.23,16.9 -63.66,34.99l-25.26,14.16 -23.9,-16.38c-17.75,-12.11 -56.32,-41.3 -115.03,-98.64 -58.88,-57.69 -88.58,-95.23 -101.04,-112.47l-16.72,-23.38L345.09,418.82c13.99,-23.9 30.72,-52.9 35.84,-62.46 4.1,-7.51 4.44,-12.63 4.44,-14.17v-0.51C374.27,324.44 303.27,219.65 290.3,201.22c-1.88,-0.34 -5.8,0 -10.75,3.42 -40.28,28.5 -82.43,68.95 -88.41,84.65C195.24,373.42 270.85,491.52 404.48,622.25c133.8,130.73 251.22,205.49 336.9,209.41 16.22,-5.63 62.81,-52.05 91.65,-91.14 2.05,-2.9 3.24,-3.58 3.24,-6.99 0,-0.51 -3.07,-15.02 -3.24,-15.53zM867.5,770.73c-0.68,1.02 -81.58,108.03 -126.12,106.67 -125.1,-3.93 -265.73,-128.86 -366.94,-227.84 -101.38,-98.99 -225.45,-238.25 -229.38,-360.96v-1.02c0,-43.01 124.76,-128 125.78,-128.69 19.62,-13.14 36.35,-13.82 52.39,10.41 7.85,10.92 87.73,123.9 96.94,138.58 3.92,6.49 11.09,25.94 11.09,34.48 0,10.92 -3.24,23.21 -9.73,34.98 -6.31,11.61 -26.62,46.59 -36.69,63.83 10.92,15.19 46.76,61.44 103.94,117.25 56.83,55.63 85.16,72.19 100.69,82.77 17.58,-9.73 53.42,-29.7 65.2,-35.67 22.18,-11.44 44.37,-12.8 59.39,-3.42 14.16,8.7 136.02,88.41 147.79,96.6 10.93,7.68 16.9,21.67 16.9,36.69 0.17,11.78 -3.58,24.41 -11.26,35.33z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:viewportHeight="1024"
android:viewportWidth="1024">
<path
android:fillColor="#fff"
android:pathData="M861.86,307.03L614.4,307.03c-31.06,-0.17 -29.86,-34.48 0,-34.14h247.46c8.54,0.17 13.31,5.46 13.49,14.17 0,8.87 -4.95,20.14 -13.49,19.97zM872.96,298.5l-126.98,130.56c-5.98,6.14 -15.7,5.98 -21.85,-0.17 -6.14,-6.32 -6.14,-16.38 -0.17,-22.36l112.3,-116.22L730.11,173.74c-6.14,-6.31 -6.14,-16.38 -0.17,-22.35 5.97,-6.14 15.7,-5.98 21.84,0.34L872.96,276.48c5.8,5.8 5.98,15.87 0,22.02zM833.02,717.99c-18.78,-12.63 -122.37,-64.85 -141.99,-77.14 -2.05,0 -7.34,0.68 -14.85,4.43 -8.02,4.1 -31.23,16.9 -63.66,34.99l-25.26,14.16 -23.9,-16.38c-17.75,-12.11 -56.32,-41.3 -115.03,-98.64 -58.88,-57.69 -88.41,-95.23 -100.86,-112.47L330.75,443.56l14.5,-24.74c14,-23.9 30.72,-52.9 35.84,-62.46 4.1,-7.51 4.44,-12.63 4.44,-14.17v-0.51C374.44,324.44 303.45,219.65 290.3,201.22c-1.88,-0.34 -5.8,0 -10.75,3.42 -40.28,28.5 -82.43,68.95 -88.41,84.65C195.24,373.42 270.85,491.52 404.48,622.25c133.63,130.73 251.05,205.49 336.9,209.41 16.22,-5.63 62.81,-52.05 91.65,-91.14 2.05,-2.9 3.24,-3.58 3.24,-6.99 0,-0.51 -3.07,-15.02 -3.24,-15.53zM867.5,770.73c-0.68,1.02 -81.58,108.03 -126.12,106.67 -125.1,-3.93 -265.56,-128.86 -366.94,-227.84 -101.38,-98.99 -225.45,-238.25 -229.38,-360.96v-1.02c0,-43.01 124.76,-128 125.78,-128.69 19.62,-13.14 36.35,-13.82 52.39,10.41 7.85,10.92 87.73,123.9 96.94,138.58 4.1,6.49 11.09,25.94 11.09,34.48 0,10.92 -3.24,23.21 -9.73,34.98 -6.14,11.61 -26.62,46.59 -36.69,63.83 10.75,15.19 46.76,61.44 103.94,117.25 56.83,55.63 85.16,72.19 100.69,82.77 17.58,-9.73 53.42,-29.7 65.2,-35.67 22.18,-11.44 44.37,-12.8 59.39,-3.42 14.16,8.7 136.19,88.41 147.79,96.6 10.93,7.68 16.9,21.67 16.9,36.69 0.17,11.78 -3.58,24.41 -11.26,35.33z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportHeight="1024"
android:viewportWidth="1024">
<path
android:fillColor="#201f1f"
android:pathData="M841.22,856.06c185.86,-185.86 185.86,-487.42 0,-673.79C655.36,-3.58 353.79,-3.58 167.42,182.27c-185.86,185.86 -185.86,487.42 0,673.79 186.37,185.86 487.94,185.86 673.79,0zM218.62,495.1c0,-47.62 111.1,-113.66 285.7,-113.66 175.1,0 285.7,66.05 285.7,113.66 0,40.96 10.75,102.4 -73.73,93.18 -84.48,-9.22 -78.85,-40.96 -78.85,-83.46 0,-29.7 -68.61,-36.35 -133.12,-36.35 -65.02,0 -133.12,6.66 -133.12,36.35 0,42.5 5.63,74.24 -78.85,83.46 -84.99,9.22 -73.73,-51.71 -73.73,-93.18z" />
</vector>

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportHeight="1024"
android:viewportWidth="1024">
<path
android:fillColor="#FFFFFF"
android:pathData="M277.9,742s167.9,-294.1 465.3,-465.3C575.7,577.8 277.9,742 277.9,742z" />
<path
android:fillColor="#FFFFFF"
android:pathData="M743.1,742S575.2,447.9 277.8,276.7C445.3,577.8 743.1,742 743.1,742z" />
<path
android:fillColor="#FFFFFF"
android:pathData="M284.1,343m-49,0a49,49 0,1 0,98 0,49 49,0 1,0 -98,0Z" />
<path
android:fillColor="#FFFFFF"
android:pathData="M284.1,515.5m-49,0a49,49 0,1 0,98 0,49 49,0 1,0 -98,0Z" />
<path
android:fillColor="#FFFFFF"
android:pathData="M284.1,683.5m-49,0a49,49 0,1 0,98 0,49 49,0 1,0 -98,0Z" />
<path
android:fillColor="#FFFFFF"
android:pathData="M808.4,341.6s-208.5,-61.2 -420,0c211.5,64.8 420,0 420,0zM808.4,516.6s-208.5,-61.2 -420,0c211.5,64.8 420,0 420,0zM808.4,684.6s-208.5,-61.2 -420,0c211.5,64.8 420,0 420,0z" />
<path
android:fillColor="#0EC469"
android:pathData="M512,512m-448,0a448,448 0,1 0,896 0,448 448,0 1,0 -896,0Z" />
<path
android:fillColor="#FFFFFF"
android:pathData="M701.4,786.8h-0.3c-3.2,-0.3 -6.4,-0.8 -9.5,-1.4C290,725.2 237.2,331.5 237.2,331.5h0.1c-5.9,-27.8 0.8,-56.8 22.5,-78.5 9.5,-9.5 22.5,-18.1 31.3,-19.2l111.6,111.6c-1.1,8.8 -9.6,21.7 -19.1,31.2 -8.5,8.5 -17.9,14.4 -28.4,18.6 57.7,214.5 272.1,271.5 272.1,271.5l0.3,0.2c4.2,-10.5 9.6,-19.3 18.1,-27.8 9.5,-9.5 22.6,-18.1 31.4,-19.2l111.6,111.6c-1.1,8.8 -9.6,21.7 -19.1,31.2 -18.9,19 -43.4,26.3 -68.2,24.1z" />
</vector>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<EditText
android:id="@+id/phone_number_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter phone number" />
<Button
android:id="@+id/dial_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Dial" />
</LinearLayout>

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".phonecallui.PhoneCallActivity">
<RelativeLayout
android:id="@+id/rl_user_info"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="@color/colorPrimaryDark">
<TextView
android:id="@+id/tv_call_number_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/tv_call_number"
android:layout_marginBottom="16dp"
android:gravity="center"
android:text="来电号码"
android:textColor="@android:color/white"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_call_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="28sp"
android:textStyle="bold"
android:layout_centerInParent="true"
tools:text="133-9527-9527" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<TextView
android:id="@+id/tv_phone_calling_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="24dp"
android:text="通话中01:33"
android:textColor="@android:color/black"
android:textSize="18sp"
android:visibility="gone"
tools:visibility="visible" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<TextView
android:id="@+id/tv_phone_hang_up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="16dp"
android:drawableTop="@drawable/ic_phone_hang_up"
android:foreground="?android:attr/selectableItemBackground"
android:gravity="center"
android:padding="8dp"
android:text="挂 断"
android:textColor="@android:color/black"
tools:visibility="visible" />
<TextView
android:id="@+id/tv_phone_pick_up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:layout_toRightOf="@id/tv_phone_hang_up"
android:drawablePadding="16dp"
android:drawableTop="@drawable/ic_phone_pick_up"
android:foreground="?android:attr/selectableItemBackground"
android:gravity="center"
android:padding="8dp"
android:text="接 听"
android:textColor="@android:color/black"
android:visibility="gone"
tools:visibility="visible" />
</RelativeLayout>
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/activitymainToolbar1"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="悬浮窗设置"
android:id="@+id/activitysettingsButton2"
android:onClick="onCanDrawOverlays"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="默认拨号设置"
android:id="@+id/activitysettingsButton1"
android:onClick="onDefaultPhone"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rl_user_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary">
<TextView
android:id="@+id/tv_call_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="30dp"
android:gravity="center"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="18sp"
tools:text="133-8892-9579" />
<Button
android:id="@+id/btn_open_app"
style="@style/Base.Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="match_parent"
android:textColor="@android:color/white"
android:layout_height="wrap_content"
android:layout_below="@id/tv_call_number"
android:gravity="center"
android:text="打开 Phone Call App"
android:textAlignment="center"
android:textSize="16sp" />
</RelativeLayout>

View File

@ -5,5 +5,8 @@
<item
android:id="@+id/item_call"
android:title="CallActivity"/>
<item
android:id="@+id/item_settings"
android:title="SettingsActivity"/>
</menu>

View File

@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyAppTheme" parent="APPBaseTheme">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
<style name="GlobalCrashActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">

View File

@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Thu Feb 20 09:27:14 GMT 2025
#Thu Feb 20 22:09:07 GMT 2025
stageCount=0
libraryProject=winboll-shared
baseVersion=1.0
publishVersion=1.0.0
buildCount=99
buildCount=126
baseBetaVersion=1.0.1