backport to Android 7.0 and 7.1

This commit is contained in:
Peter Cai 2018-08-28 09:14:53 +08:00
parent 276e53cab9
commit cf014d7978
No known key found for this signature in database
GPG key ID: 71F5FB4E4F3FD54F
9 changed files with 65 additions and 6 deletions

View file

@ -5,7 +5,7 @@ android {
buildToolsVersion '28.0.2'
defaultConfig {
applicationId "net.typeblog.shelter"
minSdkVersion 26
minSdkVersion 24
targetSdkVersion 28
versionCode 3
versionName "1.0"

View file

@ -9,6 +9,8 @@
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:maxSdkVersion="25"
android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<application
android:name=".ShelterApplication"

View file

@ -1,5 +1,6 @@
package net.typeblog.shelter.services;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
@ -11,6 +12,7 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@ -23,6 +25,7 @@ import net.typeblog.shelter.ui.DummyActivity;
import net.typeblog.shelter.util.ApplicationInfoWrapper;
import net.typeblog.shelter.util.Utility;
import java.lang.annotation.Target;
import java.util.List;
import java.util.stream.Collectors;
@ -217,8 +220,27 @@ public class ShelterService extends Service {
}
private void setForeground() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setForegroundOreo();
} else {
setForegroundLollipop();
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setForegroundLollipop() {
Notification notification = new Notification.Builder(this)
.setTicker(getString(R.string.app_name))
.setContentTitle(getString(R.string.service_title))
.setContentText(getString(R.string.service_desc))
.setSmallIcon(R.drawable.ic_notification_white_24dp)
.build();
startForeground(1, notification);
}
@TargetApi(Build.VERSION_CODES.O)
private void setForegroundOreo() {
// Android O and later: Notification Channel
// TODO: Maybe backport to pre-O?
NotificationManager nm = getSystemService(NotificationManager.class);
if (nm.getNotificationChannel(NOTIFICATION_CHANNEL_ID) == null) {
NotificationChannel chan = new NotificationChannel(

View file

@ -260,7 +260,7 @@ public class AppListFragment extends Fragment {
mService.loadIcon(app, new ILoadIconCallback.Stub() {
@Override
public void callback(Bitmap icon) {
addUnfreezeShortcut(app, icon);
getActivity().runOnUiThread(() -> addUnfreezeShortcut(app, icon));
}
});
} catch (RemoteException e) {

View file

@ -8,6 +8,7 @@ import android.content.ServiceConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.StrictMode;

View file

@ -5,6 +5,7 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
@ -315,6 +316,12 @@ public class MainActivity extends AppCompatActivity {
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (requestCode == REQUEST_PROVISION_PROFILE) {
if (resultCode == RESULT_OK) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
// For pre-Oreo, by the time this is received, the whole process
// should have completed.
recreate();
return;
}
// The sync part of the setup process is completed
// Wait for the provisioning to complete
mStorage.setBoolean(LocalStorageManager.PREF_IS_SETTING_UP, true);

View file

@ -48,7 +48,7 @@ public class LocalStorageManager {
}
public void setStringList(String pref, String[] list) {
mPrefs.edit().putString(pref, String.join(LIST_DIVIDER, list)).apply();
mPrefs.edit().putString(pref, Utility.stringJoin(LIST_DIVIDER, list)).apply();
}
public boolean stringListContains(String pref, String item) {

View file

@ -17,6 +17,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.UserManager;
import android.provider.Settings;
import android.widget.Toast;
import net.typeblog.shelter.R;
@ -31,6 +32,21 @@ import java.util.List;
import java.util.Optional;
public class Utility {
// Polyfill for String.join
public static String stringJoin(String delimiter, String[] list) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return String.join(delimiter, list);
} else {
if (list.length == 0) return "";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < list.length - 1; i++) {
sb.append(list[i]).append(delimiter);
}
sb.append(list[list.length - 1]);
return sb.toString();
}
}
// Affiliate an Intent to another profile (i.e. the Work profile that we manage)
// This method cares nothing about if the other profile even exists.
// When there is no other profile, this method would just simply throw
@ -109,6 +125,12 @@ public class Utility {
manager.clearUserRestriction(adminComponent, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
manager.clearUserRestriction(adminComponent, UserManager.DISALLOW_UNINSTALL_APPS);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
// Polyfill for UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES
// Don't use this on Android Oreo and later, it will crash
manager.setSecureSetting(adminComponent, Settings.Secure.INSTALL_NON_MARKET_APPS, "1");
}
// TODO: This should be configured by the user, instead of being enforced each time Shelter starts
// TODO: But we should also have some default restrictions that are set the first time Shelter starts
manager.addUserRestriction(adminComponent, UserManager.ALLOW_PARENT_PROFILE_APP_LINKING);
@ -177,8 +199,12 @@ public class Utility {
Toast.makeText(context, context.getString(R.string.unsupported_launcher), Toast.LENGTH_LONG).show();
}
} else {
// TODO: Maybe backport for Android < O?
throw new RuntimeException("unimplemented");
Intent shortcutIntent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent);
shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, drawableToBitmap(icon.loadDrawable(context)));
context.sendBroadcast(shortcutIntent);
Toast.makeText(context, R.string.shortcut_create_success, Toast.LENGTH_SHORT).show();
}
}
}

View file

@ -39,6 +39,7 @@
<string name="unsupported_launcher">Cannot add shortcuts to your launcher. Please contact the developer for more information.</string>
<string name="app_context_menu_title">Operations for %s</string>
<string name="freeze_all_success">All apps in \"Auto Freeze\" list have been frozen successfully.</string>
<string name="shortcut_create_success">Shortcut created on your launcher.</string>
<string name="settings">Settings</string>
<string name="settings_about">About</string>
<string name="settings_version">Version</string>