diff --git a/longan/src/main/java/com/dylanc/longan/AppInitializer.kt b/longan/src/main/java/com/dylanc/longan/AppInitializer.kt index 19e1a03..9d1eecd 100644 --- a/longan/src/main/java/com/dylanc/longan/AppInitializer.kt +++ b/longan/src/main/java/com/dylanc/longan/AppInitializer.kt @@ -26,32 +26,8 @@ class AppInitializer : Initializer { private var started = 0 override fun create(context: Context) { - application = context as Application - application.doOnActivityLifecycle( - onActivityCreated = { activity, _ -> - activityCache.add(activity) - }, - onActivityStarted = { activity -> - started++ - if (started == 1) { - onAppStatusChangedListener?.onForeground(activity) - } - }, - onActivityStopped = { activity -> - started-- - if (started == 0) { - onAppStatusChangedListener?.onBackground(activity) - } - }, - onActivityDestroyed = { activity -> - activityCache.remove(activity) - } - ) + Longan.initialize(context as Application) } override fun dependencies() = emptyList>>() - - companion object { - internal var onAppStatusChangedListener: OnAppStatusChangedListener? = null - } } diff --git a/longan/src/main/java/com/dylanc/longan/Application.kt b/longan/src/main/java/com/dylanc/longan/Application.kt index 88ae3ec..426d4e7 100644 --- a/longan/src/main/java/com/dylanc/longan/Application.kt +++ b/longan/src/main/java/com/dylanc/longan/Application.kt @@ -29,7 +29,6 @@ import android.content.res.Configuration.UI_MODE_NIGHT_MASK import android.content.res.Configuration.UI_MODE_NIGHT_YES import android.graphics.drawable.Drawable import android.net.Uri -import android.os.Process import android.provider.Settings import androidx.core.content.pm.PackageInfoCompat @@ -67,7 +66,7 @@ fun relaunchApp(killProcess: Boolean = true) = application.packageManager.getLaunchIntentForPackage(packageName)?.let { it.addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_CLEAR_TOP) startActivity(it) - if (killProcess) Process.killProcess(Process.myPid()) + if (killProcess) killProcess() } fun doOnAppStatusChanged(onForeground: ((Activity) -> Unit)? = null, onBackground: ((Activity) -> Unit)? = null) = @@ -82,7 +81,7 @@ fun doOnAppStatusChanged(onForeground: ((Activity) -> Unit)? = null, onBackgroun }) fun doOnAppStatusChanged(listener: OnAppStatusChangedListener) { - AppInitializer.onAppStatusChangedListener = listener + Longan.onAppStatusChangedListener = listener } interface OnAppStatusChangedListener { diff --git a/longan/src/main/java/com/dylanc/longan/Longan.kt b/longan/src/main/java/com/dylanc/longan/Longan.kt new file mode 100644 index 0000000..8f337ff --- /dev/null +++ b/longan/src/main/java/com/dylanc/longan/Longan.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021. Reone + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dylanc.longan + +import android.app.Application + +object Longan { + private var started = 0 + internal var onAppStatusChangedListener: OnAppStatusChangedListener? = null + + fun initialize(app: Application) { + application = app + application.doOnActivityLifecycle( + onActivityCreated = { activity, _ -> + activityCache.add(activity) + }, + onActivityStarted = { activity -> + started++ + if (started == 1) { + onAppStatusChangedListener?.onForeground(activity) + } + }, + onActivityStopped = { activity -> + started-- + if (started == 0) { + onAppStatusChangedListener?.onBackground(activity) + } + }, + onActivityDestroyed = { activity -> + activityCache.remove(activity) + } + ) + } +} \ No newline at end of file diff --git a/longan/src/main/java/com/dylanc/longan/Process.kt b/longan/src/main/java/com/dylanc/longan/Process.kt new file mode 100644 index 0000000..98a69e0 --- /dev/null +++ b/longan/src/main/java/com/dylanc/longan/Process.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021. Reone + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("unused") + +package com.dylanc.longan + +import android.annotation.SuppressLint +import android.app.ActivityManager +import android.app.Application +import android.content.Context +import android.os.Build +import android.os.Process + +fun killProcess(pid: Int = Process.myPid()) { + Process.killProcess(pid) +} + +val Context.currentProcessName: String? + get() { + if (!cacheName.isNullOrEmpty()) { + return cacheName + } + cacheName = nameFromApplication ?: nameFromActivityThread ?: nameFromActivityManager + return cacheName + } + +private var cacheName: String? = null +private val nameFromApplication: String? + get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) Application.getProcessName() else null +private val nameFromActivityThread: String? + @SuppressLint("PrivateApi") + get() { + return runCatching { + var processName: String? + Class.forName("android.app.ActivityThread", false, Application::class.java.classLoader) + .getDeclaredMethod("currentProcessName", *arrayOfNulls?>(0)).let { + it.isAccessible = true + processName = it.invoke(null, *arrayOfNulls(0)) as String + } + return processName + }.getOrNull() + } +private val Context.nameFromActivityManager: String? + get() { + val pid = Process.myPid() + (context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager)?.runningAppProcesses?.forEach { + if (it.pid == pid) { + return it.processName + } + } + return null + } \ No newline at end of file diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index c10fc40..85f0248 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -32,19 +32,22 @@ + android:name=".ui.SpannableStringActivity" + android:exported="true" /> + + android:name="androidx.core.content.FileProvider" + android:authorities="${applicationId}.provider" + android:exported="false" + android:grantUriPermissions="true"> + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/file_paths" /> diff --git a/sample/src/main/java/com/dylanc/longan/sample/App.kt b/sample/src/main/java/com/dylanc/longan/sample/App.kt index 8e67f81..e1fc219 100644 --- a/sample/src/main/java/com/dylanc/longan/sample/App.kt +++ b/sample/src/main/java/com/dylanc/longan/sample/App.kt @@ -1,9 +1,7 @@ package com.dylanc.longan.sample import android.app.Application -import com.dylanc.longan.fileProviderAuthority -import com.dylanc.longan.initLogger -import com.dylanc.longan.saveCrashLogLocally +import com.dylanc.longan.* /** * @author Dylan Cai @@ -12,6 +10,9 @@ class App : Application() { override fun onCreate() { super.onCreate() + if (currentProcessName != BuildConfig.APPLICATION_ID) { + Longan.initialize(this) + } initLogger(BuildConfig.DEBUG) saveCrashLogLocally() fileProviderAuthority = "$packageName.provider" diff --git a/sample/src/main/java/com/dylanc/longan/sample/ui/MainActivity.kt b/sample/src/main/java/com/dylanc/longan/sample/ui/MainActivity.kt index b2c30b8..e9f18fb 100644 --- a/sample/src/main/java/com/dylanc/longan/sample/ui/MainActivity.kt +++ b/sample/src/main/java/com/dylanc/longan/sample/ui/MainActivity.kt @@ -13,7 +13,7 @@ import com.dylanc.viewbinding.binding class MainActivity : AppCompatActivity() { private val binding: ActivityRecyclerViewBinding by binding() - private val items = listOf(R.string.dialogs, R.string.spannable_string, R.string.activity_result) + private val items = listOf(R.string.dialogs, R.string.spannable_string, R.string.activity_result, R.string.activity_process) private val adapter by simpleIntListAdapter { tvTitle.setText(it) } @@ -27,6 +27,7 @@ class MainActivity : AppCompatActivity() { R.string.dialogs -> startActivity() R.string.spannable_string -> startActivity() R.string.activity_result -> startActivity() + R.string.activity_process -> startActivity() } } pressBackTwiceToExitApp(R.string.exit_app) diff --git a/sample/src/main/java/com/dylanc/longan/sample/ui/ProcessActivity.kt b/sample/src/main/java/com/dylanc/longan/sample/ui/ProcessActivity.kt new file mode 100644 index 0000000..60c6cc5 --- /dev/null +++ b/sample/src/main/java/com/dylanc/longan/sample/ui/ProcessActivity.kt @@ -0,0 +1,25 @@ +package com.dylanc.longan.sample.ui + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.dylanc.longan.currentProcessName +import com.dylanc.longan.packageInfo +import com.dylanc.longan.sample.databinding.ActivityRecyclerViewBinding +import com.dylanc.longan.sample.databinding.ItemTextBinding +import com.dylanc.viewbinding.base.simpleStringListAdapter +import com.dylanc.viewbinding.binding + +class ProcessActivity : AppCompatActivity() { + + private val binding: ActivityRecyclerViewBinding by binding() + private val items = listOf(packageInfo.packageName, packageInfo.versionName, "process: $currentProcessName") + private val adapter by simpleStringListAdapter { + tvTitle.text = it + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.recyclerView.adapter = adapter + adapter.submitList(items) + } +} \ No newline at end of file diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml index fed005b..f2648f9 100644 --- a/sample/src/main/res/values/strings.xml +++ b/sample/src/main/res/values/strings.xml @@ -4,6 +4,7 @@ Dialogs SpannableString ActivityResult + OtherProcessInit selector single choice selector multi choice selector