feat(mobile-next): 导入官方 openim-flutter-demo 固定基线 3.8.3-patch.3 并完成双构建
- 上游: github.com/OpenIMSDK/openim-flutter-demo tag 3.8.3-patch.3 commit b3dfdb1e8aaeaaf6f0793e10cadd20d5c184a31f - 并行目录 mobile-next/ 原样导入(含 LICENSE),不覆盖现网 mobile/ - 锁定 Flutter 3.24.5 / Dart 3.5.4 / JDK 17 / Gradle 7.6.3 / AGP 7.3.1 - 实测 Android debug 与 release 构建均成功(见 docs/mobile-next-baseline-import.md) - 本提交可单独回退:git revert 1db1229(重写前) Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+2
@@ -0,0 +1,2 @@
|
||||
#Thu Jan 02 10:14:31 HKT 2025
|
||||
gradle.version=8.9
|
||||
@@ -0,0 +1,41 @@
|
||||
group 'io.openim.live.alert.flutter_openim_live_alert'
|
||||
version '1.0'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.allprojects {
|
||||
repositories {
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
rootProject.name = 'flutter_openim_live_alert'
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="io.openim.live.alert.flutter_openim_live_alert">
|
||||
|
||||
<uses-permission android:name="android.permission.REORDER_TASKS" />
|
||||
|
||||
</manifest>
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package io.openim.live.alert.flutter_openim_live_alert;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||
import io.flutter.plugin.common.MethodChannel.Result;
|
||||
import io.openim.live.alert.flutter_openim_live_alert.services.CallService;
|
||||
import io.openim.live.alert.flutter_openim_live_alert.utils.Utils;
|
||||
|
||||
/**
|
||||
* FlutterOpenimLiveAlertPlugin
|
||||
*/
|
||||
public class FlutterOpenimLiveAlertPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
|
||||
/// The MethodChannel that will the communication between Flutter and native Android
|
||||
///
|
||||
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||
/// when the Flutter Engine is detached from the Activity
|
||||
public static MethodChannel channel;
|
||||
private Context context;
|
||||
private Activity activity;
|
||||
|
||||
@Override
|
||||
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
||||
context = flutterPluginBinding.getApplicationContext();
|
||||
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_openim_live_alert");
|
||||
channel.setMethodCallHandler(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
||||
switch (call.method) {
|
||||
case "showLiveAlert":
|
||||
String title = call.argument("title");
|
||||
String rejectText = call.argument("rejectText");
|
||||
String acceptText = call.argument("acceptText");
|
||||
CallService.startService(activity, title, rejectText, acceptText);
|
||||
break;
|
||||
case "closeLiveAlert":
|
||||
CallService.stopService(activity);
|
||||
break;
|
||||
case "closeLiveAlertAndMoveTaskToFront": {
|
||||
CallService.stopService(activity);
|
||||
String packageName = call.argument("packageName");
|
||||
String activityName = call.argument("activityName");
|
||||
if (null == packageName) {
|
||||
packageName = activity.getPackageName();
|
||||
}
|
||||
if (null == activityName) {
|
||||
activityName = packageName + ".MainActivity";
|
||||
}
|
||||
Utils.moveTaskToFront(activity, activity.getPackageName(), activityName);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
result.notImplemented();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
|
||||
channel.setMethodCallHandler(null);
|
||||
activity = null;
|
||||
context = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
|
||||
activity = binding.getActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromActivityForConfigChanges() {
|
||||
activity = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
|
||||
activity = binding.getActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromActivity() {
|
||||
activity = null;
|
||||
}
|
||||
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package io.openim.live.alert.flutter_openim_live_alert.services;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import io.openim.live.alert.flutter_openim_live_alert.FlutterOpenimLiveAlertPlugin;
|
||||
import io.openim.live.alert.flutter_openim_live_alert.R;
|
||||
|
||||
public class CallService extends Service {
|
||||
private WindowManager mWindowManager;
|
||||
private View mFloatingView;
|
||||
private TextView mTitleView;
|
||||
private Button mRejectButton;
|
||||
private TextView mAcceptButton;
|
||||
private final Point szWindow = new Point();
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// init WindowManager
|
||||
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||
getWindowManagerDefaultDisplay();
|
||||
|
||||
// Init LayoutInflater
|
||||
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
// Inflate the floating view layout we created
|
||||
mFloatingView = inflater.inflate(R.layout.call, null);
|
||||
|
||||
mTitleView = mFloatingView.findViewById(R.id.tv_title);
|
||||
mRejectButton = mFloatingView.findViewById(R.id.btn_reject);
|
||||
mAcceptButton = mFloatingView.findViewById(R.id.btn_accept);
|
||||
mRejectButton.setOnClickListener(view -> {
|
||||
FlutterOpenimLiveAlertPlugin.channel.invokeMethod("reject", null);
|
||||
});
|
||||
mAcceptButton.setOnClickListener(view -> {
|
||||
FlutterOpenimLiveAlertPlugin.channel.invokeMethod("accept", null);
|
||||
});
|
||||
// Add the view to the window.
|
||||
WindowManager.LayoutParams params;
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
params = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_PHONE,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
} else {
|
||||
params = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
}
|
||||
|
||||
// Specify the view position
|
||||
params.gravity = Gravity.TOP | Gravity.CENTER;
|
||||
|
||||
// Initially view will be added to top-left corner, you change x-y coordinates according to your need
|
||||
params.x = 0;
|
||||
params.y = 20;
|
||||
|
||||
// Add the view to the window
|
||||
mWindowManager.addView(mFloatingView, params);
|
||||
}
|
||||
|
||||
private void getWindowManagerDefaultDisplay() {
|
||||
mWindowManager.getDefaultDisplay().getSize(szWindow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mFloatingView != null) {
|
||||
mWindowManager.removeView(mFloatingView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
String title = intent.getStringExtra("title");
|
||||
String rejectText = intent.getStringExtra("rejectText");
|
||||
String acceptText = intent.getStringExtra("acceptText");
|
||||
if (null != title) {
|
||||
mTitleView.setText(title);
|
||||
}
|
||||
if (null != rejectText) {
|
||||
mRejectButton.setText(rejectText);
|
||||
}
|
||||
if (null != acceptText) {
|
||||
mAcceptButton.setText(acceptText);
|
||||
}
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
public static void startService(Context activity, String title, String rejectText, String acceptText) {
|
||||
Intent intent = new Intent(activity, CallService.class);
|
||||
intent.putExtra("title", title);
|
||||
intent.putExtra("rejectText", rejectText);
|
||||
intent.putExtra("acceptText", acceptText);
|
||||
activity.startService(intent);
|
||||
}
|
||||
|
||||
public static void stopService(Context activity) {
|
||||
Intent intent = new Intent(activity, CallService.class);
|
||||
activity.stopService(intent);
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package io.openim.live.alert.flutter_openim_live_alert.utils;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static boolean isInstalled(Context context, String packageName) {
|
||||
try {
|
||||
final PackageManager packageManager = context.getPackageManager();
|
||||
// 获取所有已安装程序的包信息
|
||||
List<PackageInfo> infoList = packageManager.getInstalledPackages(0);
|
||||
for (int i = 0; i < infoList.size(); i++) {
|
||||
if (infoList.get(i).packageName.equalsIgnoreCase(packageName))
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isRunningForeground(Context context) {
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (null != activityManager) {
|
||||
List<ActivityManager.RunningAppProcessInfo> appProcessInfos = activityManager.getRunningAppProcesses();
|
||||
// 枚举进程
|
||||
for (ActivityManager.RunningAppProcessInfo appProcessInfo : appProcessInfos) {
|
||||
if (appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
|
||||
if (appProcessInfo.processName.equals(context.getApplicationInfo().processName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void moveTaskToFront(Context context, String packageName, String activityName) {
|
||||
//如果APP是在后台运行
|
||||
if (!isRunningForeground(context)) {
|
||||
//获取ActivityManager
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (null != activityManager) {
|
||||
//获得当前运行的task
|
||||
List<ActivityManager.RunningTaskInfo> taskList = activityManager.getRunningTasks(Integer.MAX_VALUE);
|
||||
for (ActivityManager.RunningTaskInfo rti : taskList) {
|
||||
//找到当前应用的task,并启动task的栈顶activity,达到程序切换到前台
|
||||
if (rti.topActivity.getPackageName().equals(packageName)) {
|
||||
activityManager.moveTaskToFront(rti.id, 0);
|
||||
// return;
|
||||
}
|
||||
}
|
||||
}
|
||||
//若没有找到运行的task,用户结束了task或被系统释放,则重新启动
|
||||
if (null != activityName) {
|
||||
startActivity(context, activityName);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void startActivity(Context context, String activityName) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(activityName);
|
||||
Intent intent = new Intent(context, clazz);
|
||||
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
context.startActivity(intent);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/accept_button_nor" android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/accept_button_sel" />
|
||||
</selector>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#802be2a2" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#802be2a2" />
|
||||
<corners android:radius="6dp" />
|
||||
</shape>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#2be2a2" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#2be2a2" />
|
||||
<corners android:radius="6dp" />
|
||||
</shape>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<!-- 背景颜色 -->
|
||||
<solid android:color="#b0000000" />
|
||||
|
||||
<!-- 边框线 -->
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#b0000000" />
|
||||
|
||||
<!-- 圆角 -->
|
||||
<corners android:radius="5dp" />
|
||||
|
||||
</shape>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/reject_button_nor" android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/reject_button_sel" />
|
||||
</selector>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#40FB4A42" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#40FB4A42" />
|
||||
<corners android:radius="6dp" />
|
||||
</shape>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#FB4A42" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#FB4A42" />
|
||||
<corners android:radius="6dp" />
|
||||
</shape>
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/call_background"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:text="来电提示..."
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_reject"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_below="@+id/tv_title"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_toStartOf="@+id/btn_accept"
|
||||
android:background="@drawable/reject_button"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:text="拒绝"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_accept"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignTop="@+id/btn_reject"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:background="@drawable/accept_button"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:text="接听"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="12sp" />
|
||||
</RelativeLayout>
|
||||
</FrameLayout>
|
||||
Reference in New Issue
Block a user