Redesign PrivilegedInstaller
* use new local broadcasts * show permission screen before download * display permission screen as dialog
This commit is contained in:
parent
592cd0424a
commit
2776b86050
@ -316,12 +316,18 @@
|
||||
<activity
|
||||
android:name=".privileged.views.InstallConfirmActivity"
|
||||
android:label="@string/menu_install"
|
||||
android:theme="@style/MinWithDialogBaseThemeLight"
|
||||
android:excludeFromRecents="true"
|
||||
android:parentActivityName=".FDroid"
|
||||
android:configChanges="layoutDirection|locale" >
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".FDroid" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".privileged.views.UninstallDialogActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:theme="@style/AppThemeTransparent" />
|
||||
<activity
|
||||
android:name=".views.ManageReposActivity"
|
||||
android:label="@string/app_name"
|
||||
|
@ -79,7 +79,6 @@ import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
|
||||
|
||||
import org.fdroid.fdroid.Utils.CommaSeparatedList;
|
||||
import org.fdroid.fdroid.compat.PackageManagerCompat;
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
import org.fdroid.fdroid.data.ApkProvider;
|
||||
import org.fdroid.fdroid.data.App;
|
||||
@ -88,6 +87,7 @@ import org.fdroid.fdroid.data.InstalledAppProvider;
|
||||
import org.fdroid.fdroid.data.RepoProvider;
|
||||
import org.fdroid.fdroid.installer.Installer;
|
||||
import org.fdroid.fdroid.installer.InstallManagerService;
|
||||
import org.fdroid.fdroid.installer.InstallerFactory;
|
||||
import org.fdroid.fdroid.installer.InstallerService;
|
||||
import org.fdroid.fdroid.net.Downloader;
|
||||
import org.fdroid.fdroid.net.DownloaderService;
|
||||
@ -101,6 +101,8 @@ public class AppDetails extends AppCompatActivity {
|
||||
private static final String TAG = "AppDetails";
|
||||
|
||||
private static final int REQUEST_ENABLE_BLUETOOTH = 2;
|
||||
private static final int REQUEST_PERMISSION_DIALOG = 3;
|
||||
private static final int REQUEST_UNINSTALL_DIALOG = 4;
|
||||
|
||||
public static final String EXTRA_APPID = "appid";
|
||||
public static final String EXTRA_FROM = "from";
|
||||
@ -983,6 +985,19 @@ public class AppDetails extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void initiateInstall(Apk apk) {
|
||||
Installer installer = InstallerFactory.create(this);
|
||||
Intent intent = installer.getPermissionScreen(apk);
|
||||
if (intent != null) {
|
||||
// permission screen required
|
||||
Utils.debugLog(TAG, "permission screen required");
|
||||
startActivityForResult(intent, REQUEST_PERMISSION_DIALOG);
|
||||
return;
|
||||
}
|
||||
|
||||
startInstall(apk);
|
||||
}
|
||||
|
||||
private void startInstall(Apk apk) {
|
||||
activeDownloadUrlString = apk.getUrl();
|
||||
registerDownloaderReceivers();
|
||||
headerFragment.startProgress();
|
||||
@ -990,9 +1005,22 @@ public class AppDetails extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void uninstallApk(String packageName) {
|
||||
Installer installer = InstallerFactory.create(this);
|
||||
Intent intent = installer.getUninstallScreen(packageName);
|
||||
if (intent != null) {
|
||||
// uninstall screen required
|
||||
Utils.debugLog(TAG, "screen screen required");
|
||||
startActivityForResult(intent, REQUEST_UNINSTALL_DIALOG);
|
||||
return;
|
||||
}
|
||||
|
||||
startUninstall();
|
||||
}
|
||||
|
||||
private void startUninstall() {
|
||||
localBroadcastManager.registerReceiver(uninstallReceiver,
|
||||
Installer.getUninstallIntentFilter(packageName));
|
||||
InstallerService.uninstall(context, packageName);
|
||||
Installer.getUninstallIntentFilter(app.packageName));
|
||||
InstallerService.uninstall(context, app.packageName);
|
||||
}
|
||||
|
||||
private void launchApk(String packageName) {
|
||||
@ -1016,6 +1044,18 @@ public class AppDetails extends AppCompatActivity {
|
||||
case REQUEST_ENABLE_BLUETOOTH:
|
||||
fdroidApp.sendViaBluetooth(this, resultCode, app.packageName);
|
||||
break;
|
||||
case REQUEST_PERMISSION_DIALOG:
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
Uri uri = data.getData();
|
||||
Apk apk = ApkProvider.Helper.find(this, uri, ApkProvider.DataColumns.ALL);
|
||||
startInstall(apk);
|
||||
}
|
||||
break;
|
||||
case REQUEST_UNINSTALL_DIALOG:
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
startUninstall();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,23 @@ public class FDroidApp extends Application {
|
||||
}
|
||||
}
|
||||
|
||||
public void applyDialogTheme(Activity activity) {
|
||||
activity.setTheme(getCurDialogThemeResId());
|
||||
}
|
||||
|
||||
public static int getCurDialogThemeResId() {
|
||||
switch (curTheme) {
|
||||
case light:
|
||||
return R.style.MinWithDialogBaseThemeLight;
|
||||
case dark:
|
||||
return R.style.MinWithDialogBaseThemeDark;
|
||||
case night:
|
||||
return R.style.MinWithDialogBaseThemeDark;
|
||||
default:
|
||||
return R.style.MinWithDialogBaseThemeLight;
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableSpongyCastle() {
|
||||
Security.addProvider(SPONGYCASTLE_PROVIDER);
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ import java.security.cert.CertificateEncodingException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Formatter;
|
||||
import java.util.Iterator;
|
||||
@ -457,6 +458,19 @@ public final class Utils {
|
||||
return splitter.iterator();
|
||||
}
|
||||
|
||||
public ArrayList<String> toArrayList() {
|
||||
ArrayList<String> out = new ArrayList<>();
|
||||
for (String element : this) {
|
||||
out.add(element);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public String[] toArray() {
|
||||
ArrayList<String> list = toArrayList();
|
||||
return list.toArray(new String[list.size()]);
|
||||
}
|
||||
|
||||
public boolean contains(String v) {
|
||||
for (final String s : this) {
|
||||
if (s.equals(v)) {
|
||||
|
@ -107,8 +107,12 @@ public class ApkProvider extends FDroidProvider {
|
||||
}
|
||||
|
||||
public static Apk find(Context context, String packageName, int versionCode, String[] projection) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
final Uri uri = getContentUri(packageName, versionCode);
|
||||
return find(context, uri, projection);
|
||||
}
|
||||
|
||||
public static Apk find(Context context, Uri uri, String[] projection) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||
Apk apk = null;
|
||||
if (cursor != null) {
|
||||
|
@ -85,4 +85,9 @@ public class DefaultInstaller extends Installer {
|
||||
sendBroadcastUninstall(packageName,
|
||||
Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isUnattended() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -96,4 +96,9 @@ public class ExtensionInstaller extends Installer {
|
||||
// don't use broadcasts for the rest of this special installer
|
||||
sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_COMPLETE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isUnattended() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ import org.fdroid.fdroid.data.ApkProvider;
|
||||
import org.fdroid.fdroid.data.SanitizedFile;
|
||||
import org.fdroid.fdroid.privileged.views.AppDiff;
|
||||
import org.fdroid.fdroid.privileged.views.AppSecurityPermissions;
|
||||
import org.fdroid.fdroid.privileged.views.InstallConfirmActivity;
|
||||
import org.fdroid.fdroid.privileged.views.UninstallDialogActivity;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -153,48 +155,55 @@ public abstract class Installer {
|
||||
return Uri.fromFile(sanitizedApkFile);
|
||||
}
|
||||
|
||||
public PendingIntent getPermissionScreen(Apk apk) {
|
||||
// old code:
|
||||
// Uri packageUri = Uri.fromFile(apkFile);
|
||||
// int count = newPermissionCount(packageUri);
|
||||
// if (count < 0) {
|
||||
// mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
||||
// InstallerCallback.ERROR_CODE_CANNOT_PARSE);
|
||||
public Intent getPermissionScreen(Apk apk) {
|
||||
if (!isUnattended()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// install_error_cannot_parse
|
||||
int count = newPermissionCount(apk);
|
||||
if (count > 0) {
|
||||
Uri uri = ApkProvider.getContentUri(apk);
|
||||
Intent intent = new Intent(mContext, InstallConfirmActivity.class);
|
||||
intent.setData(uri);
|
||||
|
||||
// return;
|
||||
// }
|
||||
// if (count > 0) {
|
||||
// Intent intent = new Intent(mContext, InstallConfirmActivity.class);
|
||||
// intent.setData(packageUri);
|
||||
// mActivity.startActivityForResult(intent, REQUEST_CONFIRM_PERMS);
|
||||
// } else {
|
||||
// try {
|
||||
// doInstallPackageInternal(packageUri);
|
||||
// } catch (InstallFailedException e) {
|
||||
// mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
||||
// InstallerCallback.ERROR_CODE_OTHER);
|
||||
// }
|
||||
// }
|
||||
return null;
|
||||
return intent;
|
||||
} else {
|
||||
// no permission screen needed!
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private int newPermissionCount(Apk apk) {
|
||||
// TODO: requires targetSdk in Apk class/database
|
||||
// boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion
|
||||
// >= Build.VERSION_CODES.M;
|
||||
// if (supportsRuntimePermissions) {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
private int newPermissionCount(Uri packageUri) {
|
||||
AppDiff appDiff = new AppDiff(mContext.getPackageManager(), packageUri);
|
||||
AppDiff appDiff = new AppDiff(mContext.getPackageManager(), apk);
|
||||
if (appDiff.mPkgInfo == null) {
|
||||
// could not get diff because we couldn't parse the package
|
||||
return -1;
|
||||
throw new RuntimeException("cannot parse!");
|
||||
}
|
||||
AppSecurityPermissions perms = new AppSecurityPermissions(mContext, appDiff.mPkgInfo);
|
||||
if (appDiff.mInstalledAppInfo != null) {
|
||||
// update to an existing app
|
||||
return perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW);
|
||||
}
|
||||
// default: even if there aren't any permissions, we want to make the
|
||||
// user always confirm installing new apps
|
||||
return 1;
|
||||
// new app install
|
||||
return perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
|
||||
}
|
||||
|
||||
public Intent getUninstallScreen(String packageName) {
|
||||
if (!isUnattended()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(mContext, UninstallDialogActivity.class);
|
||||
intent.putExtra(Installer.EXTRA_PACKAGE_NAME, packageName);
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -287,4 +296,6 @@ public abstract class Installer {
|
||||
|
||||
protected abstract void uninstallPackage(String packageName);
|
||||
|
||||
protected abstract boolean isUnattended();
|
||||
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ public class PrivilegedInstaller extends Installer {
|
||||
private static final HashMap<Integer, String> sUninstallReturnCodes;
|
||||
|
||||
static {
|
||||
// Descriptions extrgacted from the source code comments in AOSP
|
||||
// Descriptions extracted from the source code comments in AOSP
|
||||
sUninstallReturnCodes = new HashMap<>();
|
||||
sUninstallReturnCodes.put(DELETE_SUCCEEDED,
|
||||
"Success");
|
||||
@ -324,6 +324,7 @@ public class PrivilegedInstaller extends Installer {
|
||||
|
||||
@Override
|
||||
protected void installPackage(final Uri uri, final Uri originatingUri, String packageName) {
|
||||
sendBroadcastInstall(uri, originatingUri, Installer.ACTION_INSTALL_STARTED);
|
||||
|
||||
final Uri sanitizedUri;
|
||||
try {
|
||||
@ -374,6 +375,8 @@ public class PrivilegedInstaller extends Installer {
|
||||
|
||||
@Override
|
||||
protected void uninstallPackage(final String packageName) {
|
||||
sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_STARTED);
|
||||
|
||||
ApplicationInfo appInfo;
|
||||
try {
|
||||
//noinspection WrongConstant (lint is actually wrong here!)
|
||||
@ -466,33 +469,9 @@ public class PrivilegedInstaller extends Installer {
|
||||
Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public boolean handleOnActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
// switch (requestCode) {
|
||||
// case REQUEST_CONFIRM_PERMS:
|
||||
// if (resultCode == Activity.RESULT_OK) {
|
||||
// final Uri packageUri = data.getData();
|
||||
// try {
|
||||
// doInstallPackageInternal(packageUri);
|
||||
// } catch (InstallFailedException e) {
|
||||
// mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
||||
// InstallerCallback.ERROR_CODE_OTHER);
|
||||
// }
|
||||
// } else if (resultCode == InstallConfirmActivity.RESULT_CANNOT_PARSE) {
|
||||
// mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
||||
// InstallerCallback.ERROR_CODE_CANNOT_PARSE);
|
||||
|
||||
// install_error_cannot_parse
|
||||
|
||||
// } else { // Activity.RESULT_CANCELED
|
||||
// mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
||||
// InstallerCallback.ERROR_CODE_CANCELED);
|
||||
// }
|
||||
// return true;
|
||||
// default:
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected boolean isUnattended() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,11 +18,18 @@
|
||||
|
||||
package org.fdroid.fdroid.privileged.views;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
public class AppDiff {
|
||||
|
||||
private final PackageManager mPm;
|
||||
@ -30,6 +37,29 @@ public class AppDiff {
|
||||
|
||||
public ApplicationInfo mInstalledAppInfo;
|
||||
|
||||
/**
|
||||
* Constructor based on F-Droids Apk object
|
||||
*/
|
||||
public AppDiff(PackageManager mPm, Apk apk) {
|
||||
this.mPm = mPm;
|
||||
|
||||
if (apk.permissions == null) {
|
||||
throw new RuntimeException("apk.permissions is null");
|
||||
}
|
||||
mPkgInfo = new PackageInfo();
|
||||
mPkgInfo.packageName = apk.packageName;
|
||||
mPkgInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
// TODO: duplicate code with Permission.fdroidToAndroid
|
||||
ArrayList<String> permissionsFixed = new ArrayList<>();
|
||||
for (String perm : apk.permissions.toArrayList()) {
|
||||
permissionsFixed.add("android.permission." + perm);
|
||||
}
|
||||
mPkgInfo.requestedPermissions = permissionsFixed.toArray(new String[permissionsFixed.size()]);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
public AppDiff(PackageManager mPm, Uri mPackageURI) {
|
||||
this.mPm = mPm;
|
||||
|
||||
@ -55,7 +85,7 @@ public class AppDiff {
|
||||
String pkgName = mPkgInfo.packageName;
|
||||
// Check if there is already a package on the device with this name
|
||||
// but it has been renamed to something else.
|
||||
final String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] {pkgName});
|
||||
final String[] oldName = mPm.canonicalToCurrentPackageNames(new String[]{pkgName});
|
||||
if (oldName != null && oldName.length > 0 && oldName[0] != null) {
|
||||
pkgName = oldName[0];
|
||||
mPkgInfo.packageName = pkgName;
|
||||
|
@ -235,8 +235,7 @@ public class AppSecurityPermissions {
|
||||
try {
|
||||
installedPkgInfo = mPm.getPackageInfo(info.packageName,
|
||||
PackageManager.GET_PERMISSIONS);
|
||||
} catch (NameNotFoundException e) {
|
||||
throw new RuntimeException("NameNotFoundException during GET_PERMISSIONS!");
|
||||
} catch (NameNotFoundException ignored) {
|
||||
}
|
||||
extractPerms(info, permSet, installedPkgInfo);
|
||||
}
|
||||
|
@ -18,16 +18,16 @@
|
||||
|
||||
package org.fdroid.fdroid.privileged.views;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@ -38,15 +38,23 @@ import android.widget.ImageView;
|
||||
import android.widget.TabHost;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
|
||||
|
||||
import org.fdroid.fdroid.FDroidApp;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
import org.fdroid.fdroid.data.ApkProvider;
|
||||
import org.fdroid.fdroid.data.App;
|
||||
import org.fdroid.fdroid.data.AppProvider;
|
||||
|
||||
/**
|
||||
* NOTES:
|
||||
* Parts are based on AOSP src/com/android/packageinstaller/PackageInstallerActivity.java
|
||||
* latest included commit: c23d802958158d522e7350321ad9ac6d43013883
|
||||
*/
|
||||
public class InstallConfirmActivity extends Activity implements OnCancelListener, OnClickListener {
|
||||
public class InstallConfirmActivity extends FragmentActivity implements OnCancelListener, OnClickListener {
|
||||
|
||||
public static final int RESULT_CANNOT_PARSE = RESULT_FIRST_USER + 1;
|
||||
|
||||
@ -67,16 +75,27 @@ public class InstallConfirmActivity extends Activity implements OnCancelListener
|
||||
private static final String TAB_ID_ALL = "all";
|
||||
private static final String TAB_ID_NEW = "new";
|
||||
|
||||
private App mApp;
|
||||
|
||||
private final DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder()
|
||||
.cacheInMemory(true)
|
||||
.cacheOnDisk(true)
|
||||
.imageScaleType(ImageScaleType.NONE)
|
||||
.showImageOnLoading(R.drawable.ic_repo_app_default)
|
||||
.showImageForEmptyUri(R.drawable.ic_repo_app_default)
|
||||
.bitmapConfig(Bitmap.Config.RGB_565)
|
||||
.build();
|
||||
|
||||
private void startInstallConfirm() {
|
||||
|
||||
final Drawable appIcon = mAppDiff.mPkgInfo.applicationInfo.loadIcon(mPm);
|
||||
final String appLabel = (String) mAppDiff.mPkgInfo.applicationInfo.loadLabel(mPm);
|
||||
|
||||
View appSnippet = findViewById(R.id.app_snippet);
|
||||
((ImageView) appSnippet.findViewById(R.id.app_icon)).setImageDrawable(appIcon);
|
||||
((TextView) appSnippet.findViewById(R.id.app_name)).setText(appLabel);
|
||||
|
||||
TextView appName = (TextView) appSnippet.findViewById(R.id.app_name);
|
||||
ImageView appIcon = (ImageView) appSnippet.findViewById(R.id.app_icon);
|
||||
TabHost tabHost = (TabHost) findViewById(android.R.id.tabhost);
|
||||
|
||||
appName.setText(mApp.name);
|
||||
ImageLoader.getInstance().displayImage(mApp.iconUrlLarge, appIcon,
|
||||
displayImageOptions);
|
||||
|
||||
tabHost.setup();
|
||||
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
|
||||
TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
|
||||
@ -136,7 +155,7 @@ public class InstallConfirmActivity extends Activity implements OnCancelListener
|
||||
: R.string.install_confirm_update_no_perms;
|
||||
} else {
|
||||
// This is a new application with no permissions.
|
||||
msg = R.string.install_confirm_no_perms;
|
||||
throw new RuntimeException("no permissions requested. This screen should not appear!");
|
||||
}
|
||||
tabHost.setVisibility(View.GONE);
|
||||
findViewById(R.id.filler).setVisibility(View.VISIBLE);
|
||||
@ -171,20 +190,28 @@ public class InstallConfirmActivity extends Activity implements OnCancelListener
|
||||
protected void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
((FDroidApp) getApplication()).applyTheme(this);
|
||||
((FDroidApp) getApplication()).applyDialogTheme(this);
|
||||
|
||||
mPm = getPackageManager();
|
||||
|
||||
intent = getIntent();
|
||||
Uri packageURI = intent.getData();
|
||||
Uri uri = intent.getData();
|
||||
Apk apk = ApkProvider.Helper.find(this, uri, ApkProvider.DataColumns.ALL);
|
||||
mApp = AppProvider.Helper.findByPackageName(getContentResolver(), apk.packageName);
|
||||
|
||||
mAppDiff = new AppDiff(mPm, packageURI);
|
||||
mAppDiff = new AppDiff(mPm, apk);
|
||||
if (mAppDiff.mPkgInfo == null) {
|
||||
setResult(RESULT_CANNOT_PARSE, intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
setContentView(R.layout.install_start);
|
||||
|
||||
// increase dialog to full width for now
|
||||
// TODO: create a better design and minimum width for tablets
|
||||
getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
mInstallConfirm = findViewById(R.id.install_confirm_panel);
|
||||
mInstallConfirm.setVisibility(View.INVISIBLE);
|
||||
|
||||
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.fdroid.fdroid.privileged.views;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.ContextThemeWrapper;
|
||||
|
||||
import org.fdroid.fdroid.FDroidApp;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.installer.Installer;
|
||||
|
||||
public class UninstallDialogActivity extends FragmentActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final Intent intent = getIntent();
|
||||
final String packageName = intent.getStringExtra(Installer.EXTRA_PACKAGE_NAME);
|
||||
|
||||
PackageManager pm = getPackageManager();
|
||||
|
||||
ApplicationInfo appInfo;
|
||||
try {
|
||||
//noinspection WrongConstant (lint is actually wrong here!)
|
||||
appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new RuntimeException("Failed to get ApplicationInfo for uninstalling");
|
||||
}
|
||||
|
||||
final boolean isSystem = (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
|
||||
final boolean isUpdate = (appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
|
||||
|
||||
if (isSystem && !isUpdate) {
|
||||
// Cannot remove system apps unless we're uninstalling updates
|
||||
throw new RuntimeException("Cannot remove system apps unless we're uninstalling updates");
|
||||
}
|
||||
|
||||
int messageId;
|
||||
if (isUpdate) {
|
||||
messageId = R.string.uninstall_update_confirm;
|
||||
} else {
|
||||
messageId = R.string.uninstall_confirm;
|
||||
}
|
||||
|
||||
// hack to get theme applied (which is not automatically applied due to activity's Theme.NoDisplay
|
||||
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
|
||||
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(theme);
|
||||
builder.setTitle(appInfo.loadLabel(pm));
|
||||
builder.setIcon(appInfo.loadIcon(pm));
|
||||
builder.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent data = new Intent();
|
||||
data.putExtra(Installer.EXTRA_PACKAGE_NAME, packageName);
|
||||
setResult(Activity.RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.setMessage(messageId);
|
||||
builder.create().show();
|
||||
}
|
||||
}
|
@ -21,37 +21,35 @@
|
||||
user before it is installed.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/install_confirm"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/install_confirm"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="4dip" />
|
||||
android:id="@+id/install_confirm"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="4dip"
|
||||
android:text="@string/install_confirm"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="?android:attr/dividerHorizontal"
|
||||
android:visibility="gone" />
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="?android:attr/dividerHorizontal"
|
||||
android:visibility="gone" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/filler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:visibility="gone">
|
||||
</FrameLayout>
|
||||
android:visibility="gone"></FrameLayout>
|
||||
|
||||
<TabHost
|
||||
android:id="@android:id/tabhost"
|
||||
@ -60,24 +58,28 @@
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<HorizontalScrollView android:id="@+id/tabscontainer"
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/tabscontainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/tab_unselected_holo"
|
||||
android:fillViewport="true"
|
||||
android:scrollbars="none">
|
||||
<FrameLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TabWidget
|
||||
android:id="@android:id/tabs"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
android:layout_gravity="center"
|
||||
android:orientation="horizontal" />
|
||||
</FrameLayout>
|
||||
</HorizontalScrollView>
|
||||
|
||||
@ -85,64 +87,68 @@
|
||||
android:id="@android:id/tabcontent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="0"/>
|
||||
android:layout_weight="0" />
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
</TabHost>
|
||||
|
||||
<!-- OK confirm and cancel buttons. -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:divider="?android:attr/dividerHorizontal"
|
||||
android:showDividers="beginning">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:divider="?android:attr/dividerHorizontal"
|
||||
android:orientation="vertical"
|
||||
android:showDividers="beginning">
|
||||
|
||||
<LinearLayout
|
||||
style="?android:attr/buttonBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
style="?android:attr/buttonBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:measureWithLargestChild="true"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/leftSpacer"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.25"
|
||||
android:orientation="horizontal"
|
||||
android:measureWithLargestChild="true">
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout android:id="@+id/leftSpacer"
|
||||
android:layout_weight="0.25"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone" />
|
||||
<Button
|
||||
android:id="@+id/cancel_button"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_weight="1"
|
||||
android:maxLines="2"
|
||||
android:text="@string/cancel" />
|
||||
|
||||
<Button android:id="@+id/cancel_button"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/cancel"
|
||||
android:maxLines="2"
|
||||
style="?android:attr/buttonBarButtonStyle" />
|
||||
<Button
|
||||
android:id="@+id/ok_button"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_weight="1"
|
||||
android:filterTouchesWhenObscured="true"
|
||||
android:maxLines="2"
|
||||
android:text="@string/next" />
|
||||
|
||||
<Button android:id="@+id/ok_button"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/next"
|
||||
android:maxLines="2"
|
||||
android:filterTouchesWhenObscured="true"
|
||||
style="?android:attr/buttonBarButtonStyle" />
|
||||
|
||||
<LinearLayout android:id="@+id/rightSpacer"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="0.25"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone" />
|
||||
<LinearLayout
|
||||
android:id="@+id/rightSpacer"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.25"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2008 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -17,59 +16,46 @@
|
||||
<!--
|
||||
Defines the layout of the application snippet that appears on top of the
|
||||
installation screens
|
||||
-->
|
||||
<!-- The snippet about the application - title, icon, description. -->
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
--><!-- The snippet about the application - title, icon, description. -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/app_snippet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingStart="16dip"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingEnd="16dip"
|
||||
android:paddingTop="24dip"
|
||||
>
|
||||
<ImageView android:id="@+id/app_icon"
|
||||
android:layout_width="32dip"
|
||||
android:layout_height="32dip"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingStart="16dip"
|
||||
android:paddingTop="16dip">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/app_icon"
|
||||
android:layout_width="48dip"
|
||||
android:layout_height="48dip"
|
||||
android:layout_marginLeft="8dip"
|
||||
android:layout_marginStart="8dip"
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:gravity="start"
|
||||
android:scaleType="centerCrop"/>
|
||||
<TextView android:id="@+id/app_name"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@drawable/ic_launcher" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/app_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:paddingEnd="16dip"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingStart="16dip"
|
||||
android:shadowColor="@color/shadow"
|
||||
android:shadowRadius="2"
|
||||
android:layout_toRightOf="@id/app_icon"
|
||||
android:layout_toEndOf="@id/app_icon"
|
||||
android:singleLine="true"
|
||||
android:layout_centerInParent="true"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingEnd="16dip"
|
||||
android:paddingTop="3dip"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingStart="16dip"
|
||||
android:ellipsize="end"/>
|
||||
<FrameLayout
|
||||
android:id="@+id/top_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="4dip"
|
||||
android:layout_below="@id/app_name">
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</FrameLayout>
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
tools:text="App Name" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2008 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -21,36 +20,34 @@
|
||||
user before it is installed.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/install_confirm"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/install_confirm"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="4dip" />
|
||||
android:id="@+id/install_confirm"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="4dip"
|
||||
android:text="@string/install_confirm"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:visibility="gone" />
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/filler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:visibility="gone">
|
||||
</FrameLayout>
|
||||
android:visibility="gone"></FrameLayout>
|
||||
|
||||
<TabHost
|
||||
android:id="@android:id/tabhost"
|
||||
@ -59,24 +56,28 @@
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<HorizontalScrollView android:id="@+id/tabscontainer"
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/tabscontainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/tab_unselected_holo"
|
||||
android:fillViewport="true"
|
||||
android:scrollbars="none">
|
||||
<FrameLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TabWidget
|
||||
android:id="@android:id/tabs"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
android:layout_gravity="center"
|
||||
android:orientation="horizontal" />
|
||||
</FrameLayout>
|
||||
</HorizontalScrollView>
|
||||
|
||||
@ -84,63 +85,65 @@
|
||||
android:id="@android:id/tabcontent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="0"/>
|
||||
android:layout_weight="0" />
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
</TabHost>
|
||||
|
||||
<!-- OK confirm and cancel buttons. -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:divider="?android:attr/dividerHorizontal"
|
||||
android:showDividers="beginning">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:divider="?android:attr/dividerHorizontal"
|
||||
android:orientation="vertical"
|
||||
android:showDividers="beginning">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:measureWithLargestChild="true"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/leftSpacer"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.25"
|
||||
android:orientation="horizontal"
|
||||
android:measureWithLargestChild="true">
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout android:id="@+id/leftSpacer"
|
||||
android:layout_weight="0.25"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone" />
|
||||
<Button
|
||||
android:id="@+id/cancel_button"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_weight="1"
|
||||
android:maxLines="2"
|
||||
android:text="@string/cancel" />
|
||||
|
||||
<Button android:id="@+id/cancel_button"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/cancel"
|
||||
android:maxLines="2"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/ok_button"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_weight="1"
|
||||
android:filterTouchesWhenObscured="true"
|
||||
android:maxLines="2"
|
||||
android:text="@string/next" />
|
||||
|
||||
<Button android:id="@+id/ok_button"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/next"
|
||||
android:maxLines="2"
|
||||
android:filterTouchesWhenObscured="true"
|
||||
/>
|
||||
|
||||
<LinearLayout android:id="@+id/rightSpacer"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="0.25"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone" />
|
||||
<LinearLayout
|
||||
android:id="@+id/rightSpacer"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.25"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2008 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -14,24 +13,22 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include
|
||||
android:id="@+id/app_snippet"
|
||||
layout="@layout/install_app_details"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/app_snippet"/>
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<include
|
||||
layout="@layout/install_confirm"
|
||||
android:id="@+id/install_confirm_panel"
|
||||
layout="@layout/install_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/app_snippet"
|
||||
android:layout_alignParentBottom="true"/>
|
||||
android:layout_below="@id/app_snippet" />
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
|
@ -268,7 +268,6 @@
|
||||
<string name="root_access_denied_title">Root access denied</string>
|
||||
<string name="root_access_denied_body">Either your Android device is not rooted or you have denied root access for F-Droid.</string>
|
||||
<string name="install_error_unknown">Failed to install due to an unknown error</string>
|
||||
<string name="install_error_cannot_parse">An error occurred while parsing the package</string>
|
||||
<string name="uninstall_error_unknown">Failed to uninstall due to an unknown error</string>
|
||||
<string name="system_install_denied_title">F-Droid Privileged Extension is not available</string>
|
||||
<string name="system_install_denied_body">This option is only available when F-Droid Privileged Extension is installed.</string>
|
||||
@ -339,10 +338,7 @@
|
||||
|
||||
<string name="tap_to_install_format">Tap to install %s</string>
|
||||
<string name="tap_to_update_format">Tap to update %s</string>
|
||||
<string name="install_confirm">Do you want to install this application?
|
||||
It will get access to:</string>
|
||||
<string name="install_confirm_no_perms">Do you want to install this application?
|
||||
It does not require any special access.</string>
|
||||
<string name="install_confirm">needs access to</string>
|
||||
<string name="install_confirm_update">Do you want to install an update
|
||||
to this existing application? Your existing data will not
|
||||
be lost. The updated application will get access to:</string>
|
||||
|
@ -48,6 +48,20 @@
|
||||
<item name="colorAccent">@color/fdroid_green</item>
|
||||
</style>
|
||||
|
||||
<style name="MinWithDialogBaseThemeDark" parent="Theme.AppCompat.Dialog.MinWidth">
|
||||
<item name="colorAccent">@color/fdroid_green</item>
|
||||
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="MinWithDialogBaseThemeLight" parent="Theme.AppCompat.Light.Dialog.MinWidth">
|
||||
<item name="colorAccent">@color/fdroid_green</item>
|
||||
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="TextViewStyle" parent="android:Widget.TextView">
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
</style>
|
||||
|
Loading…
x
Reference in New Issue
Block a user