termux / termux-x11

Termux X11 add-on application.
https://termux.dev
GNU General Public License v3.0
1.96k stars 301 forks source link

We need to create "Getting started" activity... #201

Closed twaik closed 1 year ago

twaik commented 1 year ago

Hi guys. It really looks like we need to create some activity which will be started at first launch or when application lacks of permissions. Is there someone who can draw this? I can code but I have no experience in drawing layouts...

agnostic-apollo commented 1 year ago

I can code

And we are proud of you man! ;)

I don't know which permissions you need, but you can use something like following. The TermuxConstants.TERMUX_X11_* constants will be available when I push my local changes for termux-shared. The PermissionUtils provided by termux-shared can be used to check and request the permissions. Maybe in the current main activity, if certain permissions are missing, then start the TermuxX11Activity with buttons for each permission. The POST_NOTIFICATION permission should now ideally be requested by the app for Android 13. If you declare it in AndroidManifest.xml then android won't ask for it automatically, although I think targeting Android 13 may also be required for that, don't remember.

TermuxX11Activity ```java package com.termux.x11.activities; import android.content.Intent; import android.os.Bundle; import android.widget.Button; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import com.termux.x11.R; import com.termux.x11.utils.ViewUtils; import com.termux.shared.activity.media.AppCompatActivityUtils; import com.termux.shared.android.PermissionUtils; import com.termux.shared.logger.Logger; import com.termux.shared.termux.TermuxConstants; import java.util.Arrays; public class TermuxX11Activity extends AppCompatActivity { private TextView mStoragePermissionNotGrantedWarning; private TextView mBatteryOptimizationNotDisabledWarning; private TextView mDisplayOverOtherAppsPermissionNotGrantedWarning; private Button mGrantStoragePermission; private Button mDisableBatteryOptimization; private Button mGrantDisplayOverOtherAppsPermission; private static final String LOG_TAG = "TermuxX11Activity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_termux_x11); AppCompatActivityUtils.setToolbar(this, R.id.toolbar); AppCompatActivityUtils.setToolbarTitle(this, R.id.toolbar, TermuxConstants.TERMUX_X11_APP_NAME, 0); TextView pluginInfo = findViewById(R.id.textview_plugin_info); pluginInfo.setText(getString(R.string.plugin_info, TermuxConstants.TERMUX_GITHUB_REPO_URL)); mStoragePermissionNotGrantedWarning = findViewById(R.id.textview_storage_permission_not_granted_warning); mGrantStoragePermission = findViewById(R.id.btn_grant_storage_permission); mGrantStoragePermission.setOnClickListener(v -> checkAndRequestStoragePermission(true, false)); mBatteryOptimizationNotDisabledWarning = findViewById(R.id.textview_battery_optimization_not_disabled_warning); mDisableBatteryOptimization = findViewById(R.id.btn_disable_battery_optimizations); mDisableBatteryOptimization.setOnClickListener(v -> requestDisableBatteryOptimizations()); mDisplayOverOtherAppsPermissionNotGrantedWarning = findViewById(R.id.textview_display_over_other_apps_not_granted_warning); mGrantDisplayOverOtherAppsPermission = findViewById(R.id.btn_grant_display_over_other_apps_permission); mGrantDisplayOverOtherAppsPermission.setOnClickListener(v -> requestDisplayOverOtherAppsPermission()); } @Override protected void onResume() { super.onResume(); checkAndRequestStoragePermission(false, false); checkIfBatteryOptimizationNotDisabled(); checkIfDisplayOverOtherAppsPermissionNotGranted(); } /** * For processes to access primary external storage (/sdcard, /storage/emulated/0, ~/storage/shared), * termux needs to be granted legacy WRITE_EXTERNAL_STORAGE or MANAGE_EXTERNAL_STORAGE permissions * if targeting targetSdkVersion 30 (android 11) and running on sdk 30 (android 11) and higher. */ public void checkAndRequestStoragePermission(boolean requestPermission, boolean isPermissionCallback) { if (mStoragePermissionNotGrantedWarning == null) return; // Do not ask for permission again int requestCode = requestPermission ? PermissionUtils.REQUEST_GRANT_STORAGE_PERMISSION : -1; if (requestPermission) Logger.logInfo(LOG_TAG, "Requesting external storage permission"); // If permission is granted if(PermissionUtils.checkAndRequestLegacyOrManageExternalStoragePermission( this, requestCode, false)) { if (isPermissionCallback) Logger.logInfoAndShowToast(this, LOG_TAG, getString(com.termux.shared.R.string.msg_storage_permission_granted_on_request)); ViewUtils.setWarningTextViewAndButtonState(this, mStoragePermissionNotGrantedWarning, mGrantStoragePermission, false, getString(R.string.action_already_granted)); } else { if (isPermissionCallback) Logger.logInfoAndShowToast(this, LOG_TAG, getString(com.termux.shared.R.string.msg_storage_permission_not_granted_on_request)); ViewUtils.setWarningTextViewAndButtonState(this, mStoragePermissionNotGrantedWarning, mGrantStoragePermission, true, getString(R.string.action_grant_storage_permission)); } } private void checkIfBatteryOptimizationNotDisabled() { if (mBatteryOptimizationNotDisabledWarning == null) return; // If battery optimizations not disabled if (!PermissionUtils.checkIfBatteryOptimizationsDisabled(this)) { ViewUtils.setWarningTextViewAndButtonState(this, mBatteryOptimizationNotDisabledWarning, mDisableBatteryOptimization, true, getString(R.string.action_disable_battery_optimizations)); } else { ViewUtils.setWarningTextViewAndButtonState(this, mBatteryOptimizationNotDisabledWarning, mDisableBatteryOptimization, false, getString(R.string.action_already_disabled)); } } private void requestDisableBatteryOptimizations() { Logger.logDebug(LOG_TAG, "Requesting to disable battery optimizations"); PermissionUtils.requestDisableBatteryOptimizations(this, PermissionUtils.REQUEST_DISABLE_BATTERY_OPTIMIZATIONS); } private void checkIfDisplayOverOtherAppsPermissionNotGranted() { if (mDisplayOverOtherAppsPermissionNotGrantedWarning == null) return; // If display over other apps permission not granted if (!PermissionUtils.checkDisplayOverOtherAppsPermission(this)) { ViewUtils.setWarningTextViewAndButtonState(this, mDisplayOverOtherAppsPermissionNotGrantedWarning, mGrantDisplayOverOtherAppsPermission, true, getString(R.string.action_grant_display_over_other_apps_permission)); } else { ViewUtils.setWarningTextViewAndButtonState(this, mDisplayOverOtherAppsPermissionNotGrantedWarning, mGrantDisplayOverOtherAppsPermission, false, getString(R.string.action_already_granted)); } } private void requestDisplayOverOtherAppsPermission() { Logger.logDebug(LOG_TAG, "Requesting to grant display over other apps permission"); PermissionUtils.requestDisplayOverOtherAppsPermission(this, PermissionUtils.REQUEST_GRANT_DISPLAY_OVER_OTHER_APPS_PERMISSION); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); Logger.logVerbose(LOG_TAG, "onActivityResult: requestCode: " + requestCode + ", resultCode: " + resultCode + ", data: " + IntentUtils.getIntentString(data)); switch (requestCode) { case PermissionUtils.REQUEST_GRANT_STORAGE_PERMISSION: checkAndRequestStoragePermission(false, true); case PermissionUtils.REQUEST_DISABLE_BATTERY_OPTIMIZATIONS: if(PermissionUtils.checkIfBatteryOptimizationsDisabled(this)) Logger.logDebug(LOG_TAG, "Battery optimizations disabled by user on request."); else Logger.logDebug(LOG_TAG, "Battery optimizations not disabled by user on request."); break; case PermissionUtils.REQUEST_GRANT_DISPLAY_OVER_OTHER_APPS_PERMISSION: if(PermissionUtils.checkDisplayOverOtherAppsPermission(this)) Logger.logDebug(LOG_TAG, "Display over other apps granted by user on request."); else Logger.logDebug(LOG_TAG, "Display over other apps denied by user on request."); break; default: Logger.logError(LOG_TAG, "Unknown request code \"" + requestCode + "\" passed to onRequestPermissionsResult"); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); Logger.logVerbose(LOG_TAG, "onRequestPermissionsResult: requestCode: " + requestCode + ", permissions: " + Arrays.toString(permissions) + ", grantResults: " + Arrays.toString(grantResults)); if (requestCode == PermissionUtils.REQUEST_GRANT_STORAGE_PERMISSION) { checkAndRequestStoragePermission(false, true); } } } ```
ViewUtils ```java package com.termux.x11.utils; import android.content.Context; import android.widget.Button; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import com.termux.x11.R; import com.termux.shared.theme.ThemeUtils; public class ViewUtils { public static void setWarningTextViewAndButtonState(@NonNull Context context, @NonNull TextView textView, @NonNull Button button, boolean warningState, String text) { if (warningState) { textView.setTextColor(ContextCompat.getColor(context, R.color.red_error)); textView.setLinkTextColor(ContextCompat.getColor(context, R.color.red_error_link)); button.setEnabled(true); button.setAlpha(1); } else { textView.setTextColor(ThemeUtils.getTextColorPrimary(context)); textView.setLinkTextColor(ThemeUtils.getTextColorLink(context)); button.setEnabled(false); button.setAlpha(0.5f); } button.setText(text); } } ```
activity_termux_x11.xml ```xml ```
AndroidManifest.xml ```xml ```
strings.xml ```xml ]> &TERMUX_X11_APP_NAME; &TERMUX_X11_APP_NAME; is a plugin app for the &TERMUX_APP_NAME; app. \n\nCheck &TERMUX_APP_NAME; app github %1$s and &TERMUX_X11_APP_NAME; app github %2$s for more info. The storage permission must be granted to &TERMUX_X11_APP_NAME; app to... Grant Storage Permission Android battery optimizations should be disabled for the &TERMUX_X11_APP_NAME; app so that... Check https://developer.android.com/about/versions/oreo/background for more info and https://developer.android.com/guide/components/foreground-services#background-start-restrictions for more info. \n\nAlso check https://dontkillmyapp.com for info on vendor specific app killers. Depending on vendor you may need to do things like enable AutoStart, disable DuraSpeed, enable `Display pop-up windows while running in the background` for the app. Disable Battery Optimizations The display over other apps permission should be granted to &TERMUX_X11_APP_NAME; app for starting foreground activities from background. Check https://developer.android.com/guide/components/activities/background-starts for more info. Grant Draw Over Apps Permission Already Granted Already Disabled ```
twaik commented 1 year ago

You are the best @agnostic-apollo .

twaik commented 1 year ago

I am not sure if I am planning to increase api level. It will break sharing unix socket and will likely break more things.

agnostic-apollo commented 1 year ago

Lolz, you are welcome.

Yeah, that was just an FYI for termux-x11. The termux-app forks (in future) may use targetSdkVersion > 28, so I have to code and test things accordingly.

twaik commented 1 year ago

Where can I get @layout/partial_primary_toolbar, @dimen/activity_vertical_margin, @dimen/activity_horizontal_margin, @style/ViewDivider, @color/red_error, @color/red_error_link? I can create these values, but I am not sure they will fit here...

agnostic-apollo commented 1 year ago

https://github.com/termux/termux-app/wiki/Termux-Libraries

https://github.com/termux/termux-app/tree/master/termux-shared/src/main/res

twaik commented 1 year ago

It looks nice.

twaik commented 1 year ago

I've added HELP button pointing to project's README (on github) so it is not needed.