mirror of https://github.com/termux/termux-app
* Changed the hardcoded cache directory to the android alternative (pointing to /data/user/0 which is a symlink to /data/data). * Implemented the transparent activity mechanism for downloading multiple apps through the Play Store (which just works as if the intent was added to the back-stack) * Now we check deliberately check for source (external for ext. repo and play-store for the Play Store). * Fixed a few bugs.
This commit is contained in:
parent
0f078a7cf3
commit
3497119c71
|
@ -68,7 +68,7 @@ dependencies {
|
|||
testImplementation 'org.robolectric:robolectric:4.3.1'
|
||||
|
||||
//kotlin
|
||||
implementation "androidx.core:core-ktx:1.3.1"
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8'
|
||||
|
@ -181,9 +181,9 @@ task setupBootstraps() {
|
|||
doLast {
|
||||
def version = 12
|
||||
setupBootstrap("aarch64", "5e07239cad78050f56a28f9f88a0b485cead45864c6c00e1a654c728152b0244", version)
|
||||
setupBootstrap("arm", "fc72279c480c1eea46b6f0fcf78dc57599116c16dcf3b2b970a9ef828f0ec30b", version)
|
||||
setupBootstrap("i686", "895680fc967aecfa4ed77b9dc03aab95d86345be69df48402c63bfc0178337f6", version)
|
||||
setupBootstrap("x86_64", "8714ab8a5ff4e1f5f3ec01e7d0294776bfcffb187c84fa95270ec67ede8f682e", version)
|
||||
setupBootstrap("arm", "fc72279c480c1eea46b6f0fcf78dc57599116c16dcf3b2b970a9ef828f0ec30b", version)
|
||||
setupBootstrap("i686", "895680fc967aecfa4ed77b9dc03aab95d86345be69df48402c63bfc0178337f6", version)
|
||||
setupBootstrap("x86_64", "8714ab8a5ff4e1f5f3ec01e7d0294776bfcffb187c84fa95270ec67ede8f682e", version)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,10 @@
|
|||
android:resizeableActivity="true"
|
||||
android:theme="@android:style/Theme.Material.Light.DarkActionBar" />
|
||||
|
||||
|
||||
<activity
|
||||
android:name=".app.packagemanager.GooglePlayTransparentActivity"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar">
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.termux.filepicker.TermuxFileReceiverActivity"
|
||||
android:excludeFromRecents="true"
|
||||
|
|
|
@ -23,6 +23,9 @@ import android.util.Log;
|
|||
import android.widget.ArrayAdapter;
|
||||
|
||||
import com.termux.R;
|
||||
import com.termux.app.packagemanager.PackageInstaller;
|
||||
import com.termux.app.packagemanager.PackageLister;
|
||||
import com.termux.app.packagemanager.PackageUninstaller;
|
||||
import com.termux.terminal.EmulatorDebug;
|
||||
import com.termux.terminal.TerminalSession;
|
||||
import com.termux.terminal.TerminalSession.SessionChangedCallback;
|
||||
|
@ -137,11 +140,14 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
|||
Log.e(EmulatorDebug.LOG_TAG, ACTION_INSTALL_PACKAGES + " called without packages");
|
||||
} else {
|
||||
PackageInstaller packageInstaller = new PackageInstaller(this);
|
||||
if (source == null || source.isEmpty()) {
|
||||
packageInstaller.initDownloader(packages);
|
||||
} else {
|
||||
packageInstaller.downloadFromPlayStore(packages);
|
||||
}
|
||||
if (source != null) {
|
||||
if (source.equals("external")) {
|
||||
packageInstaller.initDownloader(packages);
|
||||
} else if (source.equals("play-store")) {
|
||||
packageInstaller.downloadFromPlayStore(packages);
|
||||
}
|
||||
} else
|
||||
Log.e(EmulatorDebug.LOG_TAG, ACTION_INSTALL_PACKAGES + " called without the download source!");
|
||||
}
|
||||
} else if (ACTION_LIST_PACKAGES.equals(action)) {
|
||||
PackageLister packageLister = new PackageLister(this);
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package com.termux.app.packagemanager
|
||||
|
||||
class Constants {
|
||||
companion object {
|
||||
const val PACKAGE_INSTALLED_ACTION = "com.termux.SESSION_API_PACKAGE_INSTALLED"
|
||||
const val TERMUX_CACHE_PKG_DIRECTORY_SUBFOLDER = "/pkg"
|
||||
const val APK_REPO_URL = "https://termux.net/apks/"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package com.termux.app.packagemanager
|
||||
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import com.termux.app.packagemanager.PackageInstaller.Companion.log
|
||||
|
||||
class GooglePlayTransparentActivity : Activity() {
|
||||
|
||||
private var currentPosition = 0
|
||||
private var onResumeCount = 0
|
||||
private lateinit var packageList: Array<String>
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
packageList = intent.getStringArrayExtra("packages")!!
|
||||
openStoreLink(packageList[currentPosition])
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (++onResumeCount > 1) {
|
||||
var temp = currentPosition
|
||||
if (++temp == packageList.size) {
|
||||
Toast.makeText(this, "PlayStore redirection list has exhausted. Finishing the activity...", Toast.LENGTH_SHORT).show()
|
||||
"Play Redirect Done!".log()
|
||||
finish()
|
||||
} else {
|
||||
openStoreLink(packageList[++currentPosition])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openStoreLink(packageName: String) {
|
||||
try {
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=net.termux.$packageName")).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=net.termux.$packageName")).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.termux.app
|
||||
package com.termux.app.packagemanager
|
||||
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
|
@ -6,7 +6,8 @@ import android.os.Handler
|
|||
import android.os.StatFs
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.termux.R
|
||||
import com.termux.app.PackageInstaller.Companion.log
|
||||
import com.termux.app.packagemanager.Constants.Companion.APK_REPO_URL
|
||||
import com.termux.app.packagemanager.PackageInstaller.Companion.log
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -29,7 +30,10 @@ const val NOTIFICATION_CHANNEL_ID = "termux_notification_channel"
|
|||
|
||||
class PackageDownloader(val context: Context) {
|
||||
|
||||
private lateinit var notificationManager: NotificationManager
|
||||
private val TERMUX_CACHE_DIRECTORY = "${context.cacheDir}${Constants.TERMUX_CACHE_PKG_DIRECTORY_SUBFOLDER}"
|
||||
|
||||
private lateinit
|
||||
var notificationManager: NotificationManager
|
||||
private lateinit var builder: NotificationCompat.Builder
|
||||
|
||||
interface ProgressListener {
|
||||
|
@ -66,9 +70,9 @@ class PackageDownloader(val context: Context) {
|
|||
notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
builder = NotificationCompat.Builder(context, "termux_notification_channel").setChannelId(NOTIFICATION_CHANNEL_ID)
|
||||
|
||||
File(TERMUX_CACHE_PKG_DIRECTORY).let {
|
||||
if (!it.exists()) {
|
||||
it.mkdir()
|
||||
File(TERMUX_CACHE_DIRECTORY).let { file ->
|
||||
if (!file.exists()) {
|
||||
file.mkdir()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,14 +83,14 @@ class PackageDownloader(val context: Context) {
|
|||
var percent60 = false
|
||||
var percent80 = false
|
||||
|
||||
val fileUrl = "https://termux.net/apks/$packageName.apk"
|
||||
val fileUrl = "$APK_REPO_URL$packageName.apk"
|
||||
"URL -> $fileUrl".log()
|
||||
try {
|
||||
downloadingJob = GlobalScope.launch(Dispatchers.IO) {
|
||||
val downloadData = DownloadData(packageName, 0, 0, 0, ENTERED)
|
||||
try {
|
||||
showNotification(downloadData)
|
||||
val downloadFile = File("${TERMUX_CACHE_PKG_DIRECTORY}/${packageName}.apk")
|
||||
val downloadFile = File("$TERMUX_CACHE_DIRECTORY/${packageName}.apk")
|
||||
deleteFileIfExists(downloadFile)
|
||||
"Fetching the file size...".log()
|
||||
val url = URL(fileUrl)
|
||||
|
@ -220,7 +224,7 @@ class PackageDownloader(val context: Context) {
|
|||
downloadingJob.cancel()
|
||||
}
|
||||
}
|
||||
val downloadFile = File("${TERMUX_CACHE_PKG_DIRECTORY}/${this}.apk")
|
||||
val downloadFile = File("$TERMUX_CACHE_DIRECTORY/${this}.apk")
|
||||
deleteFileIfExists(downloadFile)
|
||||
if (errorData.notificationID != 0) {
|
||||
notificationManager.cancel(errorData.notificationID)
|
|
@ -1,13 +1,19 @@
|
|||
package com.termux.app
|
||||
package com.termux.app.packagemanager
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.*
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import com.termux.app.packagemanager.Constants.Companion.PACKAGE_INSTALLED_ACTION
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -16,11 +22,10 @@ import java.io.FileInputStream
|
|||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
|
||||
const val PACKAGE_INSTALLED_ACTION = "com.termux.SESSION_API_PACKAGE_INSTALLED"
|
||||
const val TERMUX_CACHE_PKG_DIRECTORY = "/data/data/com.termux/cache/pkg"
|
||||
|
||||
class PackageInstaller(val context: Context) : PackageDownloader.ErrorListener, PackageDownloader.ProgressListener, PackageDownloader.StartListener, PackageDownloader.CompleteListener {
|
||||
|
||||
private val TERMUX_CACHE_DIRECTORY = "${context.cacheDir}${Constants.TERMUX_CACHE_PKG_DIRECTORY_SUBFOLDER}"
|
||||
private val downloadHashMap: HashMap<String, LocalDownloadData> = hashMapOf()
|
||||
private val installationResponseHashMap: HashMap<String, String> = hashMapOf()
|
||||
private var packagesToInstall: ArrayList<String> = arrayListOf()
|
||||
|
@ -30,6 +35,8 @@ class PackageInstaller(val context: Context) : PackageDownloader.ErrorListener,
|
|||
|
||||
fun initDownloader(packageList: Array<String>) {
|
||||
|
||||
TERMUX_CACHE_DIRECTORY.log()
|
||||
|
||||
if (isInstallationOfApkAllowed()) {
|
||||
context.registerReceiver(broadcastReceiver, IntentFilter(PACKAGE_INSTALLED_ACTION))
|
||||
packageDownloader.initListeners(this, this, this, this)
|
||||
|
@ -91,6 +98,7 @@ class PackageInstaller(val context: Context) : PackageDownloader.ErrorListener,
|
|||
}
|
||||
if (counter == 0) {
|
||||
endDownloadSession()
|
||||
getApkListInFileSystem()
|
||||
proceedToInstallation()
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +146,7 @@ class PackageInstaller(val context: Context) : PackageDownloader.ErrorListener,
|
|||
|
||||
private fun addApkToInstallSession(session: PackageInstaller.Session,
|
||||
packageName: String) {
|
||||
val file = File("${TERMUX_CACHE_PKG_DIRECTORY}/$packageName.apk")
|
||||
val file = File("$TERMUX_CACHE_DIRECTORY/$packageName.apk")
|
||||
val packageInSession: OutputStream = session.openWrite(packageName, 0, -1)
|
||||
val inputStream = FileInputStream(file)
|
||||
try {
|
||||
|
@ -202,7 +210,7 @@ class PackageInstaller(val context: Context) : PackageDownloader.ErrorListener,
|
|||
}
|
||||
|
||||
private fun proceedToInstallation(next: Boolean = false) {
|
||||
getApkListInFileSystem()
|
||||
|
||||
if (!next) {
|
||||
if (packagesToInstall.isEmpty()) {
|
||||
endInstallationSession()
|
||||
|
@ -223,7 +231,7 @@ class PackageInstaller(val context: Context) : PackageDownloader.ErrorListener,
|
|||
downloadHashMap.forEach { (packageName) ->
|
||||
//Setting up a default response
|
||||
installationResponseHashMap[packageName] = "the request package was either not downloaded or just doesn't exist!"
|
||||
val apkFileToBeInstalled = File("${TERMUX_CACHE_PKG_DIRECTORY}/$packageName.apk")
|
||||
val apkFileToBeInstalled = File("$TERMUX_CACHE_DIRECTORY/$packageName.apk")
|
||||
if (apkFileToBeInstalled.exists()) {
|
||||
packagesToInstall.add(packageName)
|
||||
}
|
||||
|
@ -270,21 +278,25 @@ class PackageInstaller(val context: Context) : PackageDownloader.ErrorListener,
|
|||
/*--------------------------------------- Play Store Download -----------------------------------------*/
|
||||
|
||||
fun downloadFromPlayStore(packageList: Array<String>) {
|
||||
|
||||
/*Opening multiple package links at once in Google Play will be anything but user-friendly. There's no way to prevent this but to just start a transparent
|
||||
* activity and monitor the lifecycle, but that's not a great idea in itself. */
|
||||
|
||||
fun openStoreLink(packageName: String) {
|
||||
try {
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=net.termux.$packageName")).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=net.termux.$packageName")).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||
}
|
||||
if (isGooglePlayPresent()) {
|
||||
"Google Play Store Present".log()
|
||||
context.startActivity(Intent(context, GooglePlayTransparentActivity::class.java).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK).putExtra("packages", packageList))
|
||||
} else {
|
||||
//Falling back to the external repository for downloads
|
||||
initDownloader(packageList)
|
||||
}
|
||||
packageList.forEachIndexed { _, packageName ->
|
||||
openStoreLink(packageName)
|
||||
}
|
||||
|
||||
private fun isGooglePlayPresent(): Boolean {
|
||||
return try {
|
||||
val info = context.packageManager.getPackageInfo("com.android.vending", PackageManager.GET_ACTIVITIES)
|
||||
val label = info.applicationInfo.loadLabel(context.packageManager) as String
|
||||
TextUtils.isEmpty(label) && label.startsWith("Google Play")
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class LocalDownloadData(var packageName: String, var isDownloaded: Boolean?, var extraLogs: String = "")
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.termux.app
|
||||
package com.termux.app.packagemanager
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import com.termux.app.PackageInstaller.Companion.log
|
||||
import com.termux.app.packagemanager.PackageInstaller.Companion.log
|
||||
|
||||
|
||||
class PackageLister(val context: Context) {
|
|
@ -1,11 +1,11 @@
|
|||
package com.termux.app
|
||||
package com.termux.app.packagemanager
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.Uri
|
||||
import com.termux.app.PackageInstaller.Companion.log
|
||||
import com.termux.app.packagemanager.PackageInstaller.Companion.log
|
||||
|
||||
const val TERMUX_APK_SUFFIX = "net.termux."
|
||||
|
||||
|
@ -19,7 +19,7 @@ class PackageUninstaller(var context: Context) {
|
|||
private fun uninstallAPK(packageName: String) {
|
||||
val intent = Intent(Intent.ACTION_DELETE)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
intent.data = Uri.parse("package:${TERMUX_APK_SUFFIX}${packageName}")
|
||||
intent.data = Uri.parse("package:$TERMUX_APK_SUFFIX${packageName}")
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
Loading…
Reference in New Issue