Merge pi-dev-plus-aosp-without-vendor into stage-aosp-master

Bug: 79597307
Change-Id: I048ff2f62cf3747793f8f666825a6c82cd32a867
diff --git a/FillService/AndroidManifest.xml b/FillService/AndroidManifest.xml
index f9128e1..da039b6 100644
--- a/FillService/AndroidManifest.xml
+++ b/FillService/AndroidManifest.xml
@@ -4,10 +4,16 @@
     <application>
 
         <service android:name=".FillService"
-             android:permission="android.permission.BIND_AUTOFILL">
+             android:permission="android.permission.BIND_AUTOFILL_SERVICE">
             <intent-filter>
                 <action android:name="android.service.autofill.AutofillService" />
             </intent-filter>
+
+            <meta-data
+                android:name="android.autofill"
+                android:resource="@xml/autofill_service_config">
+            </meta-data>
+
         </service>
 
         <activity android:name=".AuthActivity"/>
diff --git a/FillService/res/xml/autofill_service_config.xml b/FillService/res/xml/autofill_service_config.xml
new file mode 100644
index 0000000..dd73a19
--- /dev/null
+++ b/FillService/res/xml/autofill_service_config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 Google Inc.
+
+     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.
+-->
+
+<autofill-service xmlns:android="http://schemas.android.com/apk/res/android">
+    <compatibility-package android:name="com.android.chrome" android:maxLongVersionCode="1000000000" android:urlBarResourceId="url_bar"/>
+    <compatibility-package android:name="com.chrome.beta" android:maxLongVersionCode="1000000000" android:urlBarResourceId="url_bar"/>
+</autofill-service>
diff --git a/FillService/src/foo/bar/fill/FillService.java b/FillService/src/foo/bar/fill/FillService.java
index c253e7c..6eebac1 100644
--- a/FillService/src/foo/bar/fill/FillService.java
+++ b/FillService/src/foo/bar/fill/FillService.java
@@ -24,6 +24,7 @@
 import android.app.assist.AssistStructure.ViewNode;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.PackageManager;
 import android.os.CancellationSignal;
 import android.service.autofill.AutofillService;
 import android.service.autofill.Dataset;
@@ -33,16 +34,24 @@
 import android.service.autofill.SaveCallback;
 import android.service.autofill.SaveInfo;
 import android.service.autofill.SaveRequest;
+import android.util.Log;
 import android.view.View;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
+import android.widget.EditText;
 import android.widget.RemoteViews;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.function.Predicate;
 
+import android.widget.TextView;
 import foo.bar.fill.R;
 
 public class FillService extends AutofillService {
+    private static final String LOG_TAG = "FillService";
+
     static final boolean TEST_RESPONSE_AUTH = false;
 
     public static final String RESPONSE_ID = "RESPONSE_ID";
@@ -76,8 +85,20 @@
             @NonNull FillCallback callback) {
         AssistStructure structure = request.getFillContexts().get(0).getStructure();
 
-        ViewNode username = findUsername(structure);
-        ViewNode password = findPassword(structure);
+        dumpNodeTree(structure);
+
+//        ViewNode username = findUsername(structure);
+//        ViewNode password = findPassword(structure);
+
+        ViewNode username = null;
+        ViewNode password = null;
+        final List<ViewNode> inputs = findTextInputs(structure);
+        if (inputs.size() > 1) {
+            username = inputs.get(0);
+            password = inputs.get(1);
+        }
+
+        Log.i(LOG_TAG, "found username+username:" + (username != null && password != null));
 
         if (username != null && password != null) {
             final FillResponse response;
@@ -134,46 +155,48 @@
                                 .setValue(password.getAutofillId(),
                                         AutofillValue.forText(DATASET1_PASSWORD))
                                 .build())
-                        .addDataset(new Dataset.Builder(presentation2)
-                                .setValue(username.getAutofillId(),
-                                        AutofillValue.forText(DATASET2_USERNAME))
-                                .setValue(password.getAutofillId(),
-                                        AutofillValue.forText(DATASET2_PASSWORD))
+//                        .addDataset(new Dataset.Builder(presentation2)
+//                                .setValue(username.getAutofillId(),
+//                                        AutofillValue.forText(DATASET2_USERNAME))
+//                                .setValue(password.getAutofillId(),
+//                                        AutofillValue.forText(DATASET2_PASSWORD))
+////                                .setAuthentication(sender)
+//                                .build())
+//                        .addDataset(new Dataset.Builder(presentation3)
+//                                .setValue(username.getAutofillId(),
+//                                        AutofillValue.forText(DATASET3_USERNAME))
+//                                .setValue(password.getAutofillId(),
+//                                        AutofillValue.forText(DATASET3_PASSWORD))
+////                                .setAuthentication(sender)
+//                                .build())
+//                        .addDataset(new Dataset.Builder(presentation4)
+//                                .setValue(username.getAutofillId(),
+//                                        AutofillValue.forText(DATASET4_USERNAME))
+//                                .setValue(password.getAutofillId(),
+//                                        AutofillValue.forText(DATASET4_PASSWORD))
+////                                .setAuthentication(sender)
+//                                .build())
+//                        .addDataset(new Dataset.Builder(presentation5)
+//                                .setValue(username.getAutofillId(),
+//                                        AutofillValue.forText(DATASET5_USERNAME))
+//                                .setValue(password.getAutofillId(),
+//                                        AutofillValue.forText(DATASET5_PASSWORD))
 //                                .setAuthentication(sender)
-                                .build())
-                        .addDataset(new Dataset.Builder(presentation3)
-                                .setValue(username.getAutofillId(),
-                                        AutofillValue.forText(DATASET3_USERNAME))
-                                .setValue(password.getAutofillId(),
-                                        AutofillValue.forText(DATASET3_PASSWORD))
-//                                .setAuthentication(sender)
-                                .build())
-                        .addDataset(new Dataset.Builder(presentation4)
-                                .setValue(username.getAutofillId(),
-                                        AutofillValue.forText(DATASET4_USERNAME))
-                                .setValue(password.getAutofillId(),
-                                        AutofillValue.forText(DATASET4_PASSWORD))
-//                                .setAuthentication(sender)
-                                .build())
-                        .addDataset(new Dataset.Builder(presentation5)
-                                .setValue(username.getAutofillId(),
-                                        AutofillValue.forText(DATASET5_USERNAME))
-                                .setValue(password.getAutofillId(),
-                                        AutofillValue.forText(DATASET5_PASSWORD))
-                                .setAuthentication(sender)
-                                .build())
+//                                .build())
                         .setSaveInfo(new SaveInfo.Builder(
                                 SaveInfo.SAVE_DATA_TYPE_PASSWORD
                                         | SaveInfo.SAVE_DATA_TYPE_USERNAME,
                                 new AutofillId[] {username.getAutofillId(),
                                         password.getAutofillId()})
+                                .setFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
                                 .build())
                         .build();
             }
 
             callback.onSuccess(response);
         } else {
-            callback.onFailure("Whoops");
+            //callback.onFailure("Whoops");
+            callback.onSuccess(null);
         }
     }
 
@@ -184,20 +207,51 @@
         ViewNode password = findPassword(structure);
     }
 
+    static void dumpNodeTree(AssistStructure structure) {
+        findByPredicate(structure, (node) -> {
+            if (node.getAutofillValue() != null) {
+                Log.e("class:" + LOG_TAG, node.getClassName() + " value:" + node.getAutofillValue());
+            }
+//            Log.e(LOG_TAG, (node.getAutofillValue() != null && node.getAutofillValue().isText())
+//                    ? node.getAutofillValue().getTextValue().toString() + "-" +node.getAutofillId() : "NOPE");
+            return false;
+        });
+    }
+
+    List<ViewNode > findTextInputs(AssistStructure structure) {
+        final List<ViewNode> inputs = new ArrayList<>();
+        findByPredicate(structure, (node) -> {
+            if (node.getClassName().equals(EditText.class.getName())) {
+                inputs.add(node);
+            }
+            return false;
+        });
+        return inputs;
+    }
+
     static ViewNode findUsername(AssistStructure structure) {
         return findByPredicate(structure, (node) ->
             node.getAutofillType() == View.AUTOFILL_TYPE_TEXT
-                    && "username".equals(node.getIdEntry())
+                    && (autofillTextValueContains(node, "username")
+                            || "username".equals(node.getIdEntry()))
         );
     }
 
     static ViewNode findPassword(AssistStructure structure) {
         return findByPredicate(structure, (node) ->
-                node.getAutofillType() == View.AUTOFILL_TYPE_TEXT
-                        && "password".equals(node.getIdEntry())
+            node.getAutofillType() == View.AUTOFILL_TYPE_TEXT
+                    && (autofillTextValueContains(node, "password")
+                            || "password".equals(node.getIdEntry()))
         );
     }
 
+    private static boolean autofillTextValueContains(ViewNode node, String text) {
+        return node.getAutofillValue() != null
+                && node.getAutofillValue().getTextValue() != null
+                && node.getAutofillValue().getTextValue().toString().toLowerCase()
+                .contains(text.toLowerCase());
+    }
+
     private static ViewNode findByPredicate(AssistStructure structure,
             Predicate<ViewNode> predicate) {
         final int windowCount = structure.getWindowNodeCount();
diff --git a/KBars/Android.mk b/KBars/Android.mk
new file mode 100644
index 0000000..c79f7ac
--- /dev/null
+++ b/KBars/Android.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2012 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.
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+#LOCAL_AAPT_FLAGS += -c mdpi,hdpi,xhdpi,xxhdpi,xxhdpi
+
+LOCAL_SRC_FILES := $(call all-java-files-under, app/src/main/java)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/src/main/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-v4
+
+LOCAL_PACKAGE_NAME := KBars
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/KBars/app/build.gradle b/KBars/app/build.gradle
new file mode 100644
index 0000000..eee6df5
--- /dev/null
+++ b/KBars/app/build.gradle
@@ -0,0 +1,34 @@
+apply plugin: 'com.android.application'
+
+apply plugin: 'kotlin-android'
+
+apply plugin: 'kotlin-android-extensions'
+
+android {
+    compileSdkVersion 26
+    buildToolsVersion "26.0.0"
+    defaultConfig {
+        applicationId "js.kbars"
+        minSdkVersion 19
+        targetSdkVersion 26
+        versionCode 100
+        versionName "1.0"
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(include: ['*.jar'], dir: 'libs')
+    androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
+    implementation 'com.android.support:appcompat-v7:25.4.0'
+    testImplementation 'junit:junit:4.12'
+}
diff --git a/KBars/app/src/main/AndroidManifest.xml b/KBars/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f330c7b
--- /dev/null
+++ b/KBars/app/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="js.kbars">
+    <uses-feature android:name="android.hardware.camera" />
+    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="26" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <application android:theme="@style/AppTheme" android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:debuggable="true" android:allowBackup="true">
+        <activity android:label="@string/app_name" android:name=".KBarsActivity" android:launchMode="singleTask">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:label="DropShadowActivity" android:name=".DropShadowActivity">
+        </activity>
+        <activity android:label="(kbars-toast)" android:name=".ToastActivity" android:launchMode="singleTask">
+        </activity>
+        <service android:label="(kbars-dream)" android:name=".KBarsDream" android:exported="true">
+            <intent-filter>
+                <action android:name="android.service.dreams.DreamService" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </service>
+        <activity android:label="(kbars-car)" android:name=".KBarsCar" android:exported="true" android:launchMode="singleTask">
+            <meta-data android:name="android.dock_home" android:value="true" />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.CAR_DOCK" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/KBars/app/src/main/assets/background.png b/KBars/app/src/main/assets/background.png
new file mode 100644
index 0000000..aae8a33
--- /dev/null
+++ b/KBars/app/src/main/assets/background.png
Binary files differ
diff --git a/KBars/app/src/main/java/js/kbars/CameraBackgroundMenuItem.java b/KBars/app/src/main/java/js/kbars/CameraBackgroundMenuItem.java
new file mode 100644
index 0000000..8d25d34
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/CameraBackgroundMenuItem.java
@@ -0,0 +1,102 @@
+package js.kbars;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.View;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public final class CameraBackgroundMenuItem implements OnMenuItemClickListener {
+    public static final int REQUEST_CODE = 123;
+    private final Activity mActivity;
+    private final View mTarget;
+
+    public CameraBackgroundMenuItem(Menu menu, Activity activity, View target) {
+        this.mActivity = activity;
+        this.mTarget = target;
+        menu.add("Camera image background...").setOnMenuItemClickListener(this);
+        loadBitmap();
+    }
+
+    public boolean onMenuItemClick(MenuItem item) {
+        chooseBackground();
+        return true;
+    }
+
+    public void onActivityResult(int resultCode, Intent data) {
+        if (resultCode == -1) {
+            Bitmap bitmap = (Bitmap) data.getParcelableExtra("data");
+            if (bitmap != null) {
+                setTargetBackground(bitmap);
+                saveBitmap(bitmap);
+            }
+        }
+    }
+
+    private void chooseBackground() {
+        this.mActivity.startActivityForResult(new Intent("android.media.action.IMAGE_CAPTURE"), REQUEST_CODE);
+    }
+
+    private File bitmapFile() {
+        return new File(this.mActivity.getCacheDir(), "background.png");
+    }
+
+    private void saveBitmap(Bitmap bitmap) {
+        FileNotFoundException e;
+        Throwable th;
+        File f = bitmapFile();
+        if (f.exists()) {
+            f.delete();
+        }
+        if (bitmap != null) {
+            FileOutputStream out = null;
+            try {
+                FileOutputStream out2 = new FileOutputStream(f);
+                bitmap.compress(CompressFormat.PNG, 100, out2);
+                out2.close();
+            } catch (FileNotFoundException e1) {
+                e1.printStackTrace();
+            } catch (IOException e1) {
+                e1.printStackTrace();
+            }
+        }
+    }
+
+    private void loadBitmap() {
+        Bitmap bitmap = loadBitmapFromCache();
+        if (bitmap == null) {
+            bitmap = loadBitmapFromAssets();
+        }
+        setTargetBackground(bitmap);
+    }
+
+    private Bitmap loadBitmapFromCache() {
+        File f = bitmapFile();
+        return f.exists() ? BitmapFactory.decodeFile(f.getAbsolutePath()) : null;
+    }
+
+    private Bitmap loadBitmapFromAssets() {
+        try {
+            return BitmapFactory.decodeStream(this.mActivity.getAssets().open("background.png"));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void setTargetBackground(Bitmap bitmap) {
+        if (bitmap == null) {
+            this.mTarget.setBackground(null);
+        } else {
+            this.mTarget.setBackground(new BitmapDrawable(this.mActivity.getResources(), bitmap));
+        }
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/DropShadowActivity.java b/KBars/app/src/main/java/js/kbars/DropShadowActivity.java
new file mode 100644
index 0000000..79577d9
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/DropShadowActivity.java
@@ -0,0 +1,40 @@
+package js.kbars;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.BlurMaskFilter;
+import android.graphics.BlurMaskFilter.Blur;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+
+public class DropShadowActivity extends Activity {
+    private final Context mContext = this;
+    private ImageView mImageView;
+
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        this.mImageView = new ImageView(this.mContext);
+        this.mImageView.setBackgroundColor(-1);
+        this.mImageView.setScaleType(ScaleType.CENTER);
+        this.mImageView.setScaleX(1.0f);
+        this.mImageView.setScaleY(1.0f);
+        setContentView(this.mImageView);
+        setImage();
+    }
+
+    private void setImage() {
+        BlurMaskFilter blurFilter = new BlurMaskFilter(1.0f, Blur.SOLID);
+        Paint shadowPaint = new Paint();
+        int[] offsetXY = new int[2];
+        Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), 17301624);
+        Bitmap shadowImage32 = originalBitmap.extractAlpha(shadowPaint, offsetXY).copy(Config.ARGB_8888, true);
+        new Canvas(shadowImage32).drawBitmap(originalBitmap, (float) ((-offsetXY[0]) - 10), (float) ((-offsetXY[1]) - 10), null);
+        this.mImageView.setImageBitmap(shadowImage32);
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/FitSystemWindowsActivity.java b/KBars/app/src/main/java/js/kbars/FitSystemWindowsActivity.java
new file mode 100644
index 0000000..35ff6ae
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/FitSystemWindowsActivity.java
@@ -0,0 +1,184 @@
+package js.kbars;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnSystemUiVisibilityChangeListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class FitSystemWindowsActivity extends Activity {
+    private static final int SYSTEM_UI_CLEARABLE_FLAGS = 7;
+    private static final int SYSTEM_UI_FLAG_ALLOW_TRANSIENT = 2048;
+    private static final int SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION = 8192;
+    private static final int SYSTEM_UI_FLAG_TRANSPARENT_STATUS = 4096;
+    private final Context mContext = this;
+    private FlagStateTextView mFlagStateTextView;
+    private final Handler mHandler = new Handler();
+    private LinearLayout mLayout;
+    private final Runnable mUpdateText = new Runnable() {
+        public void run() {
+            FitSystemWindowsActivity.this.mFlagStateTextView.updateText();
+        }
+    };
+
+    private static class Flag implements Comparable<Flag> {
+        public static Flag[] ALL = find();
+        public String caption;
+        public int value;
+
+        private Flag() {
+        }
+
+        private static Flag[] find() {
+            List<Flag> flags = new ArrayList();
+            String prefix = "SYSTEM_UI_FLAG_";
+            for (Field f : View.class.getFields()) {
+                if (f.getName().startsWith(prefix)) {
+                    Flag flag = new Flag();
+                    flag.caption = f.getName().substring(prefix.length()).replace("NAVIGATION", "NAV").replace("TRANSPARENT", "TRANSP").replace("LAYOUT", "LAY");
+                    try {
+                        flag.value = f.getInt(null);
+                        if (flag.value != 0) {
+                            flags.add(flag);
+                        }
+                    } catch (Throwable t) {
+                        RuntimeException runtimeException = new RuntimeException(t);
+                    }
+                }
+            }
+            Collections.sort(flags);
+            return (Flag[]) flags.toArray(new Flag[flags.size()]);
+        }
+
+        public int compareTo(Flag another) {
+            if (this.value < another.value) {
+                return -1;
+            }
+            return this.value > another.value ? 1 : 0;
+        }
+    }
+
+    private class FlagCheckBox extends CheckBox {
+        private final Flag mFlag;
+
+        public FlagCheckBox(Context context, Flag flag) {
+            super(context);
+            setText(flag.caption);
+            this.mFlag = flag;
+        }
+    }
+
+    private class FlagSetButton extends Button {
+        public FlagSetButton(Context context) {
+            super(context);
+            setText("Set visibility");
+            setOnClickListener(new OnClickListener() {
+                public void onClick(View v) {
+                    FlagSetButton.this.set();
+                }
+            });
+        }
+
+        private void set() {
+            ViewGroup parent = (ViewGroup) getParent();
+            int n = parent.getChildCount();
+            int vis = 0;
+            for (int i = 0; i < n; i++) {
+                View v = parent.getChildAt(i);
+                if (v instanceof FlagCheckBox) {
+                    FlagCheckBox cb = (FlagCheckBox) v;
+                    if (cb.isChecked()) {
+                        vis |= cb.mFlag.value;
+                    }
+                }
+            }
+            FitSystemWindowsActivity.this.update(vis);
+        }
+    }
+
+    private class FlagStateTextView extends TextView {
+        public FlagStateTextView(Context context) {
+            super(context);
+            updateText();
+            setOnClickListener(new OnClickListener() {
+                public void onClick(View v) {
+                    FlagStateTextView.this.updateText();
+                }
+            });
+        }
+
+        private void updateText() {
+            StringBuilder sb = new StringBuilder();
+            append(sb, "vis", FitSystemWindowsActivity.this.mLayout.getSystemUiVisibility());
+            append(sb, "win", getWindowSystemUiVisibility());
+            setText(sb.toString());
+        }
+
+        private void append(StringBuilder sb, String caption, int vis) {
+            if (sb.length() > 0) {
+                sb.append('\n');
+            }
+            sb.append(caption).append(':');
+            boolean first = true;
+            for (Flag flag : Flag.ALL) {
+                if ((flag.value & vis) != 0) {
+                    if (first) {
+                        first = false;
+                    } else {
+                        sb.append(", ");
+                    }
+                    sb.append(flag.caption);
+                }
+            }
+        }
+    }
+
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        this.mLayout = new LinearLayout(this.mContext) {
+            protected boolean fitSystemWindows(Rect insets) {
+                Rect insetsBefore = new Rect(insets);
+                String msg = String.format("before=%s\nrt=%s\nafter=%s", new Object[]{insetsBefore.toShortString(), Boolean.valueOf(super.fitSystemWindows(insets)), insets.toShortString()});
+                return super.fitSystemWindows(insets);
+            }
+        };
+        this.mLayout.setOrientation(1);
+        for (Flag flag : Flag.ALL) {
+            this.mLayout.addView(new FlagCheckBox(this.mContext, flag));
+        }
+        this.mLayout.addView(new FlagSetButton(this.mContext));
+        LinearLayout linearLayout = this.mLayout;
+        View flagStateTextView = new FlagStateTextView(this.mContext);
+        this.mFlagStateTextView = (FlagStateTextView) flagStateTextView;
+        linearLayout.addView(flagStateTextView);
+        this.mLayout.setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() {
+            public void onSystemUiVisibilityChange(int vis) {
+                FitSystemWindowsActivity.this.updateTextDelayed();
+            }
+        });
+        this.mLayout.setPadding(0, 146, 0, 0);
+        setContentView(this.mLayout);
+    }
+
+    private void update(int vis) {
+        this.mLayout.setSystemUiVisibility(vis);
+        updateTextDelayed();
+    }
+
+    private void updateTextDelayed() {
+        this.mHandler.removeCallbacks(this.mUpdateText);
+        this.mHandler.postDelayed(this.mUpdateText, 500);
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/IdentifyBarsButton.java b/KBars/app/src/main/java/js/kbars/IdentifyBarsButton.java
new file mode 100644
index 0000000..18223d0
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/IdentifyBarsButton.java
@@ -0,0 +1,21 @@
+package js.kbars;
+
+import android.content.Context;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+public class IdentifyBarsButton extends Button {
+    public IdentifyBarsButton(Context context) {
+        super(context);
+        setText("Identify system bars");
+        setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                IdentifyBarsButton.this.identifyBars();
+            }
+        });
+    }
+
+    private void identifyBars() {
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/ImageBackgroundMenuItem.java b/KBars/app/src/main/java/js/kbars/ImageBackgroundMenuItem.java
new file mode 100644
index 0000000..8052d9e
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/ImageBackgroundMenuItem.java
@@ -0,0 +1,109 @@
+package js.kbars;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.View;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public final class ImageBackgroundMenuItem implements OnMenuItemClickListener {
+    public static final int REQUEST_CODE = 234;
+    private static final String TAG = ImageBackgroundMenuItem.class.getSimpleName();
+    private final Activity mActivity;
+    private final View mTarget;
+
+    public ImageBackgroundMenuItem(Menu menu, Activity activity, View target) {
+        this.mActivity = activity;
+        this.mTarget = target;
+        menu.add("Select image background...").setOnMenuItemClickListener(this);
+        loadBitmap();
+    }
+
+    public boolean onMenuItemClick(MenuItem item) {
+        chooseBackground();
+        return true;
+    }
+
+    public void onActivityResult(int resultCode, Intent data) {
+        if (resultCode == -1) {
+            Uri image = data.getData();
+            try {
+                Bitmap bitmap = BitmapFactory.decodeStream(this.mActivity.getContentResolver().openInputStream(image));
+                if (bitmap != null) {
+                    setTargetBackground(bitmap);
+                    saveBitmap(bitmap);
+                }
+            } catch (FileNotFoundException e) {
+                Log.w(TAG, "Error getting image " + image, e);
+            }
+        }
+    }
+
+    private void chooseBackground() {
+        Intent photoPickerIntent = new Intent("android.intent.action.PICK");
+        photoPickerIntent.setType("image/*");
+        this.mActivity.startActivityForResult(photoPickerIntent, REQUEST_CODE);
+    }
+
+    private File bitmapFile() {
+        return new File(this.mActivity.getCacheDir(), "background.png");
+    }
+
+    private void saveBitmap(Bitmap bitmap) {
+        FileNotFoundException e;
+        Throwable th;
+        File f = bitmapFile();
+        if (f.exists()) {
+            f.delete();
+        }
+        if (bitmap != null) {
+            try {
+                FileOutputStream out = new FileOutputStream(f);
+                bitmap.compress(CompressFormat.PNG, 100, out);
+                out.close();
+            } catch (IOException e1) {
+                e1.printStackTrace();
+            }
+        }
+    }
+
+    private void loadBitmap() {
+        Bitmap bitmap = loadBitmapFromCache();
+        if (bitmap == null) {
+            bitmap = loadBitmapFromAssets();
+        }
+        setTargetBackground(bitmap);
+    }
+
+    private Bitmap loadBitmapFromCache() {
+        File f = bitmapFile();
+        return f.exists() ? BitmapFactory.decodeFile(f.getAbsolutePath()) : null;
+    }
+
+    private Bitmap loadBitmapFromAssets() {
+        try {
+            return BitmapFactory.decodeStream(this.mActivity.getAssets().open("background.png"));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void setTargetBackground(Bitmap bitmap) {
+        if (bitmap == null) {
+            this.mTarget.setBackground(null);
+        } else {
+            this.mTarget.setBackground(new BitmapDrawable(this.mActivity.getResources(), bitmap));
+        }
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/ImmersiveModeToggleButton.java b/KBars/app/src/main/java/js/kbars/ImmersiveModeToggleButton.java
new file mode 100644
index 0000000..a904b37
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/ImmersiveModeToggleButton.java
@@ -0,0 +1,79 @@
+package js.kbars;
+
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnSystemUiVisibilityChangeListener;
+import android.widget.Button;
+
+public final class ImmersiveModeToggleButton extends Button {
+    private final KBarsActivity mActivity;
+    private final String mCaption;
+    private final int mImmersiveFlags;
+    private boolean mImmersiveMode;
+
+    public ImmersiveModeToggleButton(KBarsActivity activity, String caption, int immersiveFlags) {
+        super(activity);
+        this.mActivity = activity;
+        this.mCaption = caption;
+        this.mImmersiveFlags = immersiveFlags;
+        setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                ImmersiveModeToggleButton.this.toggleImmersiveMode("clicked");
+            }
+        });
+        updateImmersiveModeButton();
+        setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() {
+            public void onSystemUiVisibilityChange(int visibility) {
+                ImmersiveModeToggleButton.this.onSysuiChanged(visibility);
+            }
+        });
+    }
+
+    private void toggleImmersiveMode(String reason) {
+        boolean z;
+        boolean z2 = false;
+        Log.d(KBarsActivity.TAG, "toggleImmersiveMode reason=" + reason);
+        if (this.mImmersiveMode) {
+            z = false;
+        } else {
+            z = true;
+        }
+        this.mImmersiveMode = z;
+        updateImmersiveModeButton();
+        KBarsActivity kBarsActivity = this.mActivity;
+        if (!this.mImmersiveMode) {
+            z2 = true;
+        }
+        kBarsActivity.setSoloButton(z2, this, true);
+    }
+
+    private void updateImmersiveModeButton() {
+        Log.d(KBarsActivity.TAG, "updateButtons mImmersiveMode=" + this.mImmersiveMode);
+        setText(this.mImmersiveMode ? "Exit " + this.mCaption + " mode" : "Enter " + this.mCaption + " mode");
+        setSystemUiVisibility(this.mImmersiveMode ? this.mImmersiveFlags : KBarsActivity.BASE_FLAGS);
+    }
+
+    private void onSysuiChanged(int vis) {
+        boolean hideStatus;
+        boolean hideSomething = false;
+        Log.d(KBarsActivity.TAG, "sysui changed: " + Integer.toHexString(vis));
+        if ((vis & 4) != 0) {
+            hideStatus = true;
+        } else {
+            hideStatus = false;
+        }
+        boolean hideNav;
+        if ((vis & 2) != 0) {
+            hideNav = true;
+        } else {
+            hideNav = false;
+        }
+        if (hideStatus || hideNav) {
+            hideSomething = true;
+        }
+        if (this.mImmersiveMode && !hideSomething) {
+            toggleImmersiveMode("sysui_changed");
+        }
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/KBarsActivity.java b/KBars/app/src/main/java/js/kbars/KBarsActivity.java
new file mode 100644
index 0000000..33733ac
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/KBarsActivity.java
@@ -0,0 +1,273 @@
+package js.kbars;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.DisplayMetrics;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.FrameLayout.LayoutParams;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+import java.util.ArrayList;
+import java.util.List;
+
+public class KBarsActivity extends Activity {
+    public static final int BASE_FLAGS = 1792;
+    static final boolean DEBUG = true;
+    public static final int FLAG_TRANSLUCENT_NAVIGATION = 134217728;
+    public static final int FLAG_TRANSLUCENT_STATUS = 67108864;
+    private static final int IMMERSIVE_FLAGS = 3846;
+    private static final int IMMERSIVE_FLAGS_STICKY = 5894;
+    private static final int SYSTEM_UI_FLAG_IMMERSIVE = 2048;
+    private static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096;
+    static final String TAG = Util.logTag(KBarsActivity.class);
+    private List<View> mButtons = new ArrayList();
+    private CameraBackgroundMenuItem mCameraBackgroundMenuItem;
+    private final Context mContext = this;
+    private EditText mEditText;
+    private TouchTrackingLayout mFrame;
+    private ImageBackgroundMenuItem mImageBackgroundMenuItem;
+
+    private static final class DebugButton extends Button {
+        private final Handler mHandler = new Handler();
+        private final Runnable mNavTransOff = new Runnable() {
+            public void run() {
+                DebugButton.this.setNavTrans(false);
+            }
+        };
+        private final Runnable mNavTransOn = new Runnable() {
+            public void run() {
+                DebugButton.this.setNavTrans(true);
+                DebugButton.this.cancelTrans();
+                DebugButton.this.mHandler.postDelayed(DebugButton.this.mNavTransOff, 5000);
+            }
+        };
+
+        public DebugButton(Context context) {
+            super(context);
+            setText("Debug");
+            setOnClickListener(new OnClickListener() {
+                public void onClick(View v) {
+                    DebugButton.this.fallingToast();
+                }
+            });
+        }
+
+        private void cancelTrans() {
+            this.mHandler.removeCallbacks(this.mNavTransOn);
+            this.mHandler.removeCallbacks(this.mNavTransOff);
+        }
+
+        private void setNavTrans(boolean trans) {
+            Window w = ((Activity) getContext()).getWindow();
+            if (trans) {
+                w.addFlags(KBarsActivity.FLAG_TRANSLUCENT_STATUS);
+            } else {
+                w.clearFlags(KBarsActivity.FLAG_TRANSLUCENT_STATUS);
+            }
+        }
+
+        public void fallingToast() {
+            this.mNavTransOff.run();
+            Toast.makeText(getContext(), "Here is a toast", 1).show();
+            cancelTrans();
+            this.mHandler.postDelayed(this.mNavTransOn, 1000);
+        }
+    }
+
+    private static final class InvisibleButton extends Button {
+        public InvisibleButton(Context context) {
+            super(context);
+            super.setVisibility(4);
+        }
+
+        public void setVisibility(int visibility) {
+        }
+    }
+
+    protected void onCreate(Bundle savedInstanceState) {
+        boolean portrait;
+        int i = 0;
+        super.onCreate(savedInstanceState);
+        getWindow().requestFeature(9);
+        DisplayMetrics dm = new DisplayMetrics();
+        getWindowManager().getDefaultDisplay().getRealMetrics(dm);
+        Toast.makeText(this.mContext, dm.widthPixels + "x" + dm.heightPixels + " supported=" + areTranslucentBarsSupported(), 0).show();
+        getActionBar().setTitle("kbars " + Util.getVersionName(this.mContext));
+        getActionBar().setBackgroundDrawable(new ColorDrawable(0));
+        this.mFrame = new TouchTrackingLayout(this.mContext);
+        LinearLayout allButtons = new LinearLayout(this.mContext);
+        Configuration config = getResources().getConfiguration();
+        if (((float) config.screenWidthDp) / ((float) config.screenHeightDp) < 1.0f) {
+            portrait = true;
+        } else {
+            portrait = false;
+        }
+        if (portrait) {
+            i = 1;
+        }
+        allButtons.setOrientation(i);
+        LinearLayout buttons1 = new LinearLayout(this.mContext);
+        buttons1.setOrientation(1);
+        LinearLayout buttons2 = new LinearLayout(this.mContext);
+        buttons2.setOrientation(1);
+        allButtons.addView(buttons1);
+        allButtons.addView(buttons2);
+        LayoutParams buttonsLP = new LayoutParams(-2, -2);
+        buttonsLP.gravity = 17;
+        this.mFrame.addView(allButtons, buttonsLP);
+        this.mEditText = new EditText(this.mContext);
+        this.mEditText.setVisibility(8);
+        buttons1.addView(this.mEditText, -1, -2);
+        buttons1.addView(new TransparencyToggleButton(this.mContext, "status bar", FLAG_TRANSLUCENT_STATUS));
+        buttons1.addView(new TransparencyToggleButton(this.mContext, "navigation bar", FLAG_TRANSLUCENT_NAVIGATION));
+        buttons1.addView(new InvisibleButton(this.mContext));
+        buttons2.addView(new ImmersiveModeToggleButton(this, "immersive", IMMERSIVE_FLAGS));
+        buttons2.addView(new ImmersiveModeToggleButton(this, "sticky immersive", IMMERSIVE_FLAGS_STICKY));
+        buttons2.addView(new LightsOutModeToggleButton(this.mContext));
+        buttons2.addView(new MediaModeToggleButton(this.mContext, this.mFrame));
+        buttons2.addView(new InvisibleButton(this.mContext));
+        setContentView(this.mFrame);
+        addButtons(buttons1);
+        addButtons(buttons2);
+    }
+
+    private void addButtons(LinearLayout buttons) {
+        for (int i = 0; i < buttons.getChildCount(); i++) {
+            this.mButtons.add(buttons.getChildAt(i));
+        }
+    }
+
+    private boolean areTranslucentBarsSupported() {
+        int id = getResources().getIdentifier("config_enableTranslucentDecor", "bool", "android");
+        if (id == 0) {
+            return false;
+        }
+        return getResources().getBoolean(id);
+    }
+
+    public boolean onCreateOptionsMenu(Menu menu) {
+        RandomColorBackgroundMenuItem randomColorBackgroundMenuItem = new RandomColorBackgroundMenuItem(menu, this.mFrame);
+        this.mImageBackgroundMenuItem = new ImageBackgroundMenuItem(menu, this, this.mFrame);
+        this.mCameraBackgroundMenuItem = new CameraBackgroundMenuItem(menu, this, this.mFrame);
+        createDebugMenuItem(menu);
+        createEditTextMenuItem(menu);
+        createOrientationMenuItem(menu, R.string.action_portrait, 1);
+        createOrientationMenuItem(menu, R.string.action_landscape, 0);
+        createOrientationMenuItem(menu, R.string.action_reverse_portrait, 9);
+        createOrientationMenuItem(menu, R.string.action_reverse_landscape, 8);
+        return true;
+    }
+
+    private void createOrientationMenuItem(Menu menu, int text, final int orientation) {
+        MenuItem mi = menu.add(text);
+        mi.setShowAsAction(0);
+        mi.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+            public boolean onMenuItemClick(MenuItem item) {
+                KBarsActivity.this.setRequestedOrientation(orientation);
+                return true;
+            }
+        });
+    }
+
+    private void createDebugMenuItem(Menu menu) {
+        final MenuItem mi = menu.add("Show gesture debugging");
+        mi.setShowAsActionFlags(0);
+        mi.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+            public boolean onMenuItemClick(MenuItem item) {
+                boolean debug;
+                if (KBarsActivity.this.mFrame.getDebug()) {
+                    debug = false;
+                } else {
+                    debug = true;
+                }
+                KBarsActivity.this.mFrame.setDebug(debug);
+                mi.setTitle(new StringBuilder(String.valueOf(debug ? "Hide" : "Show")).append(" gesture debugging").toString());
+                return true;
+            }
+        });
+    }
+
+    private void createEditTextMenuItem(Menu menu) {
+        final MenuItem mi = menu.add("Show EditText");
+        mi.setShowAsActionFlags(0);
+        mi.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+            public boolean onMenuItemClick(MenuItem item) {
+                boolean isVisible;
+                int i = 0;
+                if (KBarsActivity.this.mEditText.getVisibility() == 0) {
+                    isVisible = true;
+                } else {
+                    isVisible = false;
+                }
+                EditText access$1 = KBarsActivity.this.mEditText;
+                if (isVisible) {
+                    i = 8;
+                }
+                access$1.setVisibility(i);
+                mi.setTitle(new StringBuilder(String.valueOf(isVisible ? "Show" : "Hide")).append(" EditText").toString());
+                return true;
+            }
+        });
+    }
+
+    public void setSoloButton(boolean visible, View solo, boolean animate) {
+        int i;
+        int vis = 0;
+        if (visible) {
+            i = 1;
+        } else {
+            i = 0;
+        }
+        float alpha = (float) i;
+        if (!visible) {
+            vis = 4;
+        }
+        final int _vis = vis;
+        for (final View view : this.mButtons) {
+            if (!(view == solo || (view instanceof EditText))) {
+                view.setEnabled(visible);
+                if (animate) {
+                    view.animate().alpha(alpha).setListener(new AnimatorListener() {
+                        public void onAnimationCancel(Animator animation) {
+                        }
+
+                        public void onAnimationEnd(Animator animation) {
+                            view.setVisibility(_vis);
+                        }
+
+                        public void onAnimationRepeat(Animator animation) {
+                        }
+
+                        public void onAnimationStart(Animator animation) {
+                        }
+                    }).start();
+                } else {
+                    view.setVisibility(vis);
+                }
+            }
+        }
+    }
+
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == CameraBackgroundMenuItem.REQUEST_CODE) {
+            this.mCameraBackgroundMenuItem.onActivityResult(resultCode, data);
+        }
+        if (requestCode == ImageBackgroundMenuItem.REQUEST_CODE) {
+            this.mImageBackgroundMenuItem.onActivityResult(resultCode, data);
+        }
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/KBarsCar.java b/KBars/app/src/main/java/js/kbars/KBarsCar.java
new file mode 100644
index 0000000..c280c95
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/KBarsCar.java
@@ -0,0 +1,38 @@
+package js.kbars;
+
+import android.app.Activity;
+import android.app.UiModeManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+public class KBarsCar extends Activity {
+    private static final String TAG = Util.logTag(KBarsCar.class);
+    private final Context mContext = this;
+    private View mView;
+
+    protected void onCreate(Bundle savedInstanceState) {
+        requestWindowFeature(1);
+        super.onCreate(savedInstanceState);
+        this.mView = new View(this.mContext);
+        this.mView.setBackgroundColor(-65536);
+        setFullscreen();
+        setContentView(this.mView);
+        this.mView.setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                ((UiModeManager) KBarsCar.this.mContext.getSystemService("uimode")).disableCarMode(1);
+                KBarsCar.this.finish();
+            }
+        });
+    }
+
+    protected void onResume() {
+        super.onResume();
+        setFullscreen();
+    }
+
+    private void setFullscreen() {
+        this.mView.setSystemUiVisibility(5382);
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/KBarsDream.java b/KBars/app/src/main/java/js/kbars/KBarsDream.java
new file mode 100644
index 0000000..7a4c92b
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/KBarsDream.java
@@ -0,0 +1,47 @@
+package js.kbars;
+
+import android.content.Context;
+import android.os.Handler;
+import android.service.dreams.DreamService;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnSystemUiVisibilityChangeListener;
+
+public class KBarsDream extends DreamService {
+    private static final String TAG = Util.logTag(KBarsDream.class);
+    private final Context mContext = this;
+    private final Handler mHandler = new Handler();
+    private View mView;
+
+    public void onCreate() {
+        super.onCreate();
+        setInteractive(true);
+    }
+
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        this.mView = new View(this.mContext);
+        getWindow().addFlags(1024);
+        setFullscreen();
+        this.mView.setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() {
+            public void onSystemUiVisibilityChange(int visibility) {
+                Log.d(KBarsDream.TAG, "onSystemUiVisibilityChange " + Integer.toHexString(visibility));
+                KBarsDream.this.setFullscreen();
+            }
+        });
+        this.mView.setBackgroundColor(-16776961);
+        setContentView(this.mView);
+    }
+
+    public void onWindowFocusChanged(boolean hasFocus) {
+        Log.d(TAG, "onWindowFocusChanged " + hasFocus);
+        super.onWindowFocusChanged(hasFocus);
+        if (hasFocus) {
+            setFullscreen();
+        }
+    }
+
+    private void setFullscreen() {
+        this.mView.setSystemUiVisibility(5382);
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/LightsOutModeToggleButton.java b/KBars/app/src/main/java/js/kbars/LightsOutModeToggleButton.java
new file mode 100644
index 0000000..9593142
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/LightsOutModeToggleButton.java
@@ -0,0 +1,26 @@
+package js.kbars;
+
+import android.content.Context;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+public final class LightsOutModeToggleButton extends Button {
+    private boolean mLightsOut;
+
+    public LightsOutModeToggleButton(Context context) {
+        super(context);
+        setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                LightsOutModeToggleButton.this.mLightsOut = !LightsOutModeToggleButton.this.mLightsOut;
+                LightsOutModeToggleButton.this.update();
+            }
+        });
+        update();
+    }
+
+    private void update() {
+        setText(new StringBuilder(String.valueOf(this.mLightsOut ? "Exit" : "Enter")).append(" lights out mode").toString());
+        setSystemUiVisibility(this.mLightsOut ? 1 : 0);
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/MediaModeToggleButton.java b/KBars/app/src/main/java/js/kbars/MediaModeToggleButton.java
new file mode 100644
index 0000000..1e2b19d
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/MediaModeToggleButton.java
@@ -0,0 +1,95 @@
+package js.kbars;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.net.Uri;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnSystemUiVisibilityChangeListener;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
+import android.widget.VideoView;
+
+public final class MediaModeToggleButton extends Button {
+    private static final int MEDIA_FLAGS = 1798;
+    private final FrameLayout mFrame;
+    private boolean mMediaMode;
+    private VideoView mVideo;
+
+    public MediaModeToggleButton(Context context, FrameLayout frame) {
+        super(context);
+        this.mFrame = frame;
+        setText("Enter media mode");
+        setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                MediaModeToggleButton.this.setSystemUiVisibility(MediaModeToggleButton.MEDIA_FLAGS);
+            }
+        });
+        setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() {
+            public void onSystemUiVisibilityChange(int vis) {
+                MediaModeToggleButton.this.updateSysUi(vis);
+            }
+        });
+        initVideo();
+        updateSysUi(0);
+    }
+
+    private void initVideo() {
+        this.mVideo = new VideoView(getContext());
+        this.mVideo.setVisibility(8);
+        LayoutParams videoLP = new LayoutParams(-1, -1);
+        videoLP.gravity = 17;
+        this.mFrame.addView(this.mVideo, videoLP);
+        this.mVideo.setOnPreparedListener(new OnPreparedListener() {
+            public void onPrepared(MediaPlayer mp) {
+                mp.setLooping(true);
+            }
+        });
+        this.mVideo.setVideoURI(Uri.parse("android.resource://" + getContext().getPackageName() + "/" + R.raw.clipcanvas));
+        this.mVideo.setBackgroundColor(0);
+    }
+
+    private void updateSysUi(int vis) {
+        boolean requested;
+        boolean hideStatus;
+        boolean hideNav;
+        boolean hideSomething;
+        if (getSystemUiVisibility() == MEDIA_FLAGS) {
+            requested = true;
+        } else {
+            requested = false;
+        }
+        if ((vis & 4) != 0) {
+            hideStatus = true;
+        } else {
+            hideStatus = false;
+        }
+        if ((vis & 2) != 0) {
+            hideNav = true;
+        } else {
+            hideNav = false;
+        }
+        if (hideStatus || hideNav) {
+            hideSomething = true;
+        } else {
+            hideSomething = false;
+        }
+        Log.d(KBarsActivity.TAG, String.format("vis change hideStatus=%s hideNav=%s hideSomething=%s mMediaMode=%s", new Object[]{Boolean.valueOf(hideStatus), Boolean.valueOf(hideNav), Boolean.valueOf(hideSomething), Boolean.valueOf(this.mMediaMode)}));
+        this.mMediaMode = false;
+        if (requested && hideStatus && hideNav) {
+            this.mMediaMode = true;
+        } else {
+            setSystemUiVisibility(KBarsActivity.BASE_FLAGS);
+        }
+        if (this.mMediaMode) {
+            this.mVideo.setVisibility(0);
+            this.mVideo.start();
+            return;
+        }
+        this.mVideo.setVisibility(8);
+        this.mVideo.stopPlayback();
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/RandomColorBackgroundMenuItem.java b/KBars/app/src/main/java/js/kbars/RandomColorBackgroundMenuItem.java
new file mode 100644
index 0000000..d1895f3
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/RandomColorBackgroundMenuItem.java
@@ -0,0 +1,29 @@
+package js.kbars;
+
+import android.graphics.Color;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.View;
+import java.util.Random;
+
+public class RandomColorBackgroundMenuItem implements OnMenuItemClickListener {
+    private static final Random RANDOM = new Random();
+    private final View mTarget;
+
+    public RandomColorBackgroundMenuItem(Menu menu, View target) {
+        this.mTarget = target;
+        MenuItem mi = menu.add("Random color background");
+        mi.setShowAsActionFlags(0);
+        mi.setOnMenuItemClickListener(this);
+    }
+
+    private void setColorBackground() {
+        this.mTarget.setBackgroundColor(Color.rgb(RANDOM.nextInt(255), RANDOM.nextInt(255), RANDOM.nextInt(255)));
+    }
+
+    public boolean onMenuItemClick(MenuItem item) {
+        setColorBackground();
+        return true;
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/SystemGestureDebugger.java b/KBars/app/src/main/java/js/kbars/SystemGestureDebugger.java
new file mode 100644
index 0000000..3992a82
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/SystemGestureDebugger.java
@@ -0,0 +1,216 @@
+package js.kbars;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.MotionEvent;
+
+public class SystemGestureDebugger {
+    private static final boolean DEBUG = false;
+    private static final int MAX_TRACKED_POINTERS = 32;
+    private static final int SWIPE_FROM_BOTTOM = 2;
+    private static final int SWIPE_FROM_RIGHT = 3;
+    private static final int SWIPE_FROM_TOP = 1;
+    private static final int SWIPE_NONE = 0;
+    private static final long SWIPE_TIMEOUT_MS = 500;
+    private static final String TAG = Util.logTag(SystemGestureDebugger.class);
+    private static final int UNTRACKED_POINTER = -1;
+    private final Callbacks mCallbacks;
+    private final float[] mCurrentX = new float[MAX_TRACKED_POINTERS];
+    private final float[] mCurrentY = new float[MAX_TRACKED_POINTERS];
+    private boolean mDebugFireable;
+    private final int[] mDownPointerId = new int[MAX_TRACKED_POINTERS];
+    private int mDownPointers;
+    private final long[] mDownTime = new long[MAX_TRACKED_POINTERS];
+    private final float[] mDownX = new float[MAX_TRACKED_POINTERS];
+    private final float[] mDownY = new float[MAX_TRACKED_POINTERS];
+    private final long[] mElapsedThreshold = new long[MAX_TRACKED_POINTERS];
+    private final int mSwipeDistanceThreshold;
+    private boolean mSwipeFireable;
+    private final int mSwipeStartThreshold;
+    int screenHeight;
+    int screenWidth;
+
+    interface Callbacks {
+        void onDebug();
+
+        void onSwipeFromBottom();
+
+        void onSwipeFromRight();
+
+        void onSwipeFromTop();
+    }
+
+    public static class DownPointerInfo {
+        public float currentX;
+        public float currentY;
+        public int downPointerId;
+        public long downTime;
+        public float downX;
+        public float downY;
+        public long elapsedThreshold;
+    }
+
+    public static class ThresholdInfo {
+        public int distance;
+        public long elapsed;
+        public int start;
+    }
+
+    public SystemGestureDebugger(Context context, Callbacks callbacks) {
+        this.mCallbacks = (Callbacks) checkNull("callbacks", callbacks);
+        this.mSwipeStartThreshold = ((Context) checkNull("context", context)).getResources().getDimensionPixelSize(((Integer) Util.getField("com.android.internal.R$dimen", "status_bar_height")).intValue());
+        this.mSwipeDistanceThreshold = this.mSwipeStartThreshold;
+        Log.d(TAG, String.format("startThreshold=%s endThreshold=%s", new Object[]{Integer.valueOf(this.mSwipeStartThreshold), Integer.valueOf(this.mSwipeDistanceThreshold)}));
+    }
+
+    private static <T> T checkNull(String name, T arg) {
+        if (arg != null) {
+            return arg;
+        }
+        throw new IllegalArgumentException(new StringBuilder(String.valueOf(name)).append(" must not be null").toString());
+    }
+
+    public void onPointerEvent(MotionEvent event) {
+        boolean z = true;
+        boolean z2 = DEBUG;
+        switch (event.getActionMasked()) {
+            case SWIPE_NONE /*0*/:
+                this.mSwipeFireable = true;
+                this.mDebugFireable = true;
+                this.mDownPointers = SWIPE_NONE;
+                captureDown(event, SWIPE_NONE);
+                return;
+            case 1:
+            case SWIPE_FROM_RIGHT /*3*/:
+                this.mSwipeFireable = DEBUG;
+                this.mDebugFireable = DEBUG;
+                return;
+            case 2:
+                if (this.mSwipeFireable) {
+                    int swipe = detectSwipe(event);
+                    if (swipe == 0) {
+                        z2 = true;
+                    }
+                    this.mSwipeFireable = z2;
+                    if (swipe == 1) {
+                        this.mCallbacks.onSwipeFromTop();
+                        return;
+                    } else if (swipe == 2) {
+                        this.mCallbacks.onSwipeFromBottom();
+                        return;
+                    } else if (swipe == SWIPE_FROM_RIGHT) {
+                        this.mCallbacks.onSwipeFromRight();
+                        return;
+                    } else {
+                        return;
+                    }
+                }
+                return;
+            case 5:
+                captureDown(event, event.getActionIndex());
+                if (this.mDebugFireable) {
+                    if (event.getPointerCount() >= 5) {
+                        z = DEBUG;
+                    }
+                    this.mDebugFireable = z;
+                    if (!this.mDebugFireable) {
+                        this.mCallbacks.onDebug();
+                        return;
+                    }
+                    return;
+                }
+                return;
+            default:
+                return;
+        }
+    }
+
+    private void captureDown(MotionEvent event, int pointerIndex) {
+        int i = findIndex(event.getPointerId(pointerIndex));
+        if (i != UNTRACKED_POINTER) {
+            this.mDownX[i] = event.getX(pointerIndex);
+            this.mDownY[i] = event.getY(pointerIndex);
+            this.mDownTime[i] = event.getEventTime();
+        }
+    }
+
+    private int findIndex(int pointerId) {
+        for (int i = SWIPE_NONE; i < this.mDownPointers; i++) {
+            if (this.mDownPointerId[i] == pointerId) {
+                return i;
+            }
+        }
+        if (this.mDownPointers == MAX_TRACKED_POINTERS || pointerId == UNTRACKED_POINTER) {
+            return UNTRACKED_POINTER;
+        }
+        int[] iArr = this.mDownPointerId;
+        int i2 = this.mDownPointers;
+        this.mDownPointers = i2 + 1;
+        iArr[i2] = pointerId;
+        return this.mDownPointers + UNTRACKED_POINTER;
+    }
+
+    private int detectSwipe(MotionEvent move) {
+        int historySize = move.getHistorySize();
+        int pointerCount = move.getPointerCount();
+        for (int p = SWIPE_NONE; p < pointerCount; p++) {
+            int i = findIndex(move.getPointerId(p));
+            if (i != UNTRACKED_POINTER) {
+                int swipe;
+                for (int h = SWIPE_NONE; h < historySize; h++) {
+                    swipe = detectSwipe(i, move.getHistoricalEventTime(h), move.getHistoricalX(p, h), move.getHistoricalY(p, h));
+                    if (swipe != 0) {
+                        return swipe;
+                    }
+                }
+                swipe = detectSwipe(i, move.getEventTime(), move.getX(p), move.getY(p));
+                if (swipe != 0) {
+                    return swipe;
+                }
+            }
+        }
+        return SWIPE_NONE;
+    }
+
+    private int detectSwipe(int i, long time, float x, float y) {
+        float fromX = this.mDownX[i];
+        float fromY = this.mDownY[i];
+        long elapsed = time - this.mDownTime[i];
+        this.mCurrentX[i] = x;
+        this.mCurrentY[i] = y;
+        boolean reachedThreshold = ((fromY > ((float) this.mSwipeStartThreshold) || y <= ((float) this.mSwipeDistanceThreshold) + fromY) && ((fromY < ((float) (this.screenHeight - this.mSwipeStartThreshold)) || y >= fromY - ((float) this.mSwipeDistanceThreshold)) && (fromX < ((float) (this.screenWidth - this.mSwipeStartThreshold)) || x >= fromX - ((float) this.mSwipeDistanceThreshold)))) ? DEBUG : true;
+        if (!reachedThreshold) {
+            this.mElapsedThreshold[i] = elapsed;
+        }
+        if (fromY <= ((float) this.mSwipeStartThreshold) && y > ((float) this.mSwipeDistanceThreshold) + fromY && elapsed < SWIPE_TIMEOUT_MS) {
+            return 1;
+        }
+        if (fromY >= ((float) (this.screenHeight - this.mSwipeStartThreshold)) && y < fromY - ((float) this.mSwipeDistanceThreshold) && elapsed < SWIPE_TIMEOUT_MS) {
+            return 2;
+        }
+        if (fromX < ((float) (this.screenWidth - this.mSwipeStartThreshold)) || x >= fromX - ((float) this.mSwipeDistanceThreshold) || elapsed >= SWIPE_TIMEOUT_MS) {
+            return SWIPE_NONE;
+        }
+        return SWIPE_FROM_RIGHT;
+    }
+
+    public int getDownPointers() {
+        return this.mDownPointers;
+    }
+
+    public void getDownPointerInfo(int index, DownPointerInfo out) {
+        out.downPointerId = this.mDownPointerId[index];
+        out.downX = this.mDownX[index];
+        out.downY = this.mDownY[index];
+        out.downTime = this.mDownTime[index];
+        out.currentX = this.mCurrentX[index];
+        out.currentY = this.mCurrentY[index];
+        out.elapsedThreshold = this.mElapsedThreshold[index];
+    }
+
+    public void getThresholdInfo(ThresholdInfo out) {
+        out.start = this.mSwipeStartThreshold;
+        out.distance = this.mSwipeDistanceThreshold;
+        out.elapsed = SWIPE_TIMEOUT_MS;
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/ToastActivity.java b/KBars/app/src/main/java/js/kbars/ToastActivity.java
new file mode 100644
index 0000000..18f9a89
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/ToastActivity.java
@@ -0,0 +1,117 @@
+package js.kbars;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnSystemUiVisibilityChangeListener;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+public class ToastActivity extends Activity {
+    private static final int ALLOW_TRANSIENT = 2048;
+    private static final String TAG = Util.logTag(ToastActivity.class);
+    private View mContent;
+    private final Context mContext = this;
+    boolean mImmersive;
+
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        LinearLayout buttons = new LinearLayout(this.mContext);
+        buttons.setOrientation(1);
+        for (final Method m : getClass().getDeclaredMethods()) {
+            if (Modifier.isPublic(m.getModifiers()) && m.getParameterTypes().length == 0) {
+                Button btn = new Button(this.mContext);
+                btn.setText(m.getName());
+                btn.setOnClickListener(new OnClickListener() {
+                    public void onClick(View v) {
+                        try {
+                            m.invoke(ToastActivity.this.mContext, new Object[0]);
+                        } catch (Throwable t) {
+                            Log.w(ToastActivity.TAG, "Error running " + m.getName(), t);
+                        }
+                    }
+                });
+                buttons.addView(btn);
+            }
+        }
+        setContentView(buttons);
+        this.mContent = buttons;
+        setSysui();
+        this.mContent.setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() {
+            public void onSystemUiVisibilityChange(int visibility) {
+                if ((visibility & 2) == 0) {
+                    ToastActivity.this.mImmersive = false;
+                    ToastActivity.this.setSysui();
+                }
+            }
+        });
+    }
+
+    public void toast1() {
+        Toast.makeText(this.mContext, "toast!", 0).show();
+    }
+
+    public void toast2() {
+        Toast t = Toast.makeText(this.mContext, "toast!", 0);
+        TextView tv = new TextView(this.mContext);
+        tv.setBackgroundColor(-65536);
+        tv.setText("setView");
+        t.setView(tv);
+        t.show();
+    }
+
+    public void toast3() {
+        Toast t = Toast.makeText(this.mContext, "toast!", 0);
+        TextView tv = new TextView(this.mContext) {
+            protected boolean fitSystemWindows(Rect insets) {
+                Rect before = new Rect(insets);
+                boolean rt = super.fitSystemWindows(insets);
+                Log.d(ToastActivity.TAG, String.format("before=%s rt=%s after=%s", new Object[]{before.toShortString(), Boolean.valueOf(rt), insets.toShortString()}));
+                return rt;
+            }
+        };
+        Log.d(TAG, "fitsSystemWindows=" + tv.getFitsSystemWindows());
+        tv.setFitsSystemWindows(true);
+        tv.setSystemUiVisibility(768);
+        tv.setBackgroundColor(-65536);
+        tv.setText("setView");
+        t.setView(tv);
+        t.show();
+    }
+
+    public void hideNav() {
+        this.mContent.setSystemUiVisibility(2);
+    }
+
+    public void dangerToast() {
+        Toast t = Toast.makeText(this.mContext, "toast!", 0);
+        TextView tv = new TextView(this.mContext);
+        tv.setSystemUiVisibility(512);
+        tv.setBackgroundColor(-65536);
+        tv.setText("setView");
+        t.setView(tv);
+        t.setGravity(80, 0, 90);
+        t.show();
+    }
+
+    public void toggleImmersive() {
+        this.mImmersive = !this.mImmersive;
+        setSysui();
+    }
+
+    private void setSysui() {
+        int flags = 2560;
+        if (this.mImmersive) {
+            flags = 2560 | 2;
+        }
+        this.mContent.setSystemUiVisibility(flags);
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/TouchTrackingLayout.java b/KBars/app/src/main/java/js/kbars/TouchTrackingLayout.java
new file mode 100644
index 0000000..83e6c30
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/TouchTrackingLayout.java
@@ -0,0 +1,298 @@
+package js.kbars;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Cap;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnLayoutChangeListener;
+import android.widget.FrameLayout;
+import java.util.ArrayList;
+import java.util.List;
+import js.kbars.SystemGestureDebugger.DownPointerInfo;
+import js.kbars.SystemGestureDebugger.ThresholdInfo;
+
+public class TouchTrackingLayout extends FrameLayout {
+    private static final boolean DEBUG_EVENTS = true;
+    private static final int MAX_INFO_LINES = 6;
+    private static final int MAX_POINTERS = 5;
+    private static final int MAX_POINTS = 5000;
+    private static final String TAG = Util.logTag(TouchTrackingLayout.class);
+    private boolean mDebug;
+    private final DownPointerInfo mDownPointerInfo = new DownPointerInfo();
+    private final SystemGestureDebugger mGestures;
+    private final Paint mInfoPaint = new Paint();
+    private final StringBuilder[] mInfoText = allocateInfoText();
+    private final int mInfoTextLineHeight;
+    private final int mInfoTextOffset;
+    private final int[] mScribbleCounts = new int[MAX_POINTERS];
+    private final Paint mScribblePaint = new Paint();
+    private final List<float[]> mScribblePoints = allocatePoints();
+    private final int mTextSize;
+    private final ThresholdInfo mThresholdInfo = new ThresholdInfo();
+    private final float[] mThresholdLines = new float[16];
+    private final Paint mThresholdPaint = new Paint();
+
+    public TouchTrackingLayout(Context context) {
+        super(context);
+        setWillNotDraw(false);
+        int densityDpi = Util.getDensityDpi(context);
+        this.mTextSize = (int) (((float) densityDpi) / 12.0f);
+        this.mInfoTextLineHeight = (int) (((float) densityDpi) / 11.0f);
+        this.mInfoTextOffset = (int) (((float) densityDpi) / 1.7f);
+        Log.d(TAG, "mTextSize=" + this.mTextSize + " mInfoTextLineHeight=" + this.mInfoTextLineHeight + " mInfoTextOffset=" + this.mInfoTextOffset);
+        this.mScribblePaint.setColor(-13388315);
+        this.mScribblePaint.setStrokeWidth((float) this.mTextSize);
+        this.mScribblePaint.setStrokeCap(Cap.ROUND);
+        this.mScribblePaint.setStyle(Style.STROKE);
+        this.mGestures = new SystemGestureDebugger(context, new SystemGestureDebugger.Callbacks() {
+            public void onSwipeFromTop() {
+                Log.d(TouchTrackingLayout.TAG, "GestureCallbacks.onSwipeFromTop");
+            }
+
+            public void onSwipeFromRight() {
+                Log.d(TouchTrackingLayout.TAG, "GestureCallbacks.onSwipeFromRight");
+            }
+
+            public void onSwipeFromBottom() {
+                Log.d(TouchTrackingLayout.TAG, "GestureCallbacks.onSwipeFromBottom");
+            }
+
+            public void onDebug() {
+                Log.d(TouchTrackingLayout.TAG, "GestureCallbacks.onDebug");
+            }
+        });
+        this.mGestures.getThresholdInfo(this.mThresholdInfo);
+        this.mInfoPaint.setTypeface(Typeface.MONOSPACE);
+        this.mInfoPaint.setTextSize((float) this.mTextSize);
+        this.mInfoPaint.setStyle(Style.STROKE);
+        this.mInfoPaint.setColor(-13388315);
+        this.mThresholdPaint.setColor(-13388315);
+        this.mThresholdPaint.setStrokeWidth(1.0f);
+        this.mThresholdPaint.setStyle(Style.STROKE);
+        addOnLayoutChangeListener(new OnLayoutChangeListener() {
+            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                TouchTrackingLayout.this.mGestures.screenWidth = right - left;
+                TouchTrackingLayout.this.mGestures.screenHeight = bottom - top;
+                TouchTrackingLayout.this.horLine(0, TouchTrackingLayout.this.mThresholdInfo.start);
+                TouchTrackingLayout.this.horLine(1, TouchTrackingLayout.this.mGestures.screenHeight - TouchTrackingLayout.this.mThresholdInfo.start);
+                TouchTrackingLayout.this.verLine(2, TouchTrackingLayout.this.mThresholdInfo.start);
+                TouchTrackingLayout.this.verLine(3, TouchTrackingLayout.this.mGestures.screenWidth - TouchTrackingLayout.this.mThresholdInfo.start);
+            }
+        });
+    }
+
+    public boolean getDebug() {
+        return this.mDebug;
+    }
+
+    public void setDebug(boolean debug) {
+        this.mDebug = debug;
+        invalidate();
+    }
+
+    protected boolean fitSystemWindows(Rect insets) {
+        Rect insetsBefore = new Rect(insets);
+        boolean rt = super.fitSystemWindows(insets);
+        Log.d(TAG, String.format("fitSystemWindows insetsBefore=%s insetsAfter=%s rt=%s", new Object[]{insetsBefore.toShortString(), insets.toShortString(), Boolean.valueOf(rt)}));
+        return rt;
+    }
+
+    private void horLine(int lineNum, int y) {
+        this.mThresholdLines[(lineNum * 4) + 0] = 0.0f;
+        float[] fArr = this.mThresholdLines;
+        int i = (lineNum * 4) + 1;
+        float f = (float) y;
+        this.mThresholdLines[(lineNum * 4) + 3] = f;
+        fArr[i] = f;
+        this.mThresholdLines[(lineNum * 4) + 2] = (float) this.mGestures.screenWidth;
+    }
+
+    private void verLine(int lineNum, int x) {
+        float[] fArr = this.mThresholdLines;
+        int i = (lineNum * 4) + 0;
+        float f = (float) x;
+        this.mThresholdLines[(lineNum * 4) + 2] = f;
+        fArr[i] = f;
+        this.mThresholdLines[(lineNum * 4) + 1] = 0.0f;
+        this.mThresholdLines[(lineNum * 4) + 3] = (float) this.mGestures.screenHeight;
+    }
+
+    private static StringBuilder[] allocateInfoText() {
+        StringBuilder[] rt = new StringBuilder[MAX_INFO_LINES];
+        for (int i = 0; i < rt.length; i++) {
+            rt[i] = new StringBuilder();
+        }
+        return rt;
+    }
+
+    public boolean onTouchEvent(MotionEvent ev) {
+        super.onTouchEvent(ev);
+        this.mGestures.onPointerEvent(ev);
+        computeInfoText();
+        onMotionEvent(ev);
+        return true;
+    }
+
+    protected void onDraw(Canvas canvas) {
+        int i;
+        for (i = 0; i < this.mScribbleCounts.length; i++) {
+            int c = this.mScribbleCounts[i];
+            if (c > 0) {
+                canvas.drawLines((float[]) this.mScribblePoints.get(i), 0, c - 2, this.mScribblePaint);
+            }
+        }
+        if (this.mDebug) {
+            int leftMargin = this.mThresholdInfo.start + (this.mTextSize / 4);
+            for (i = 0; i < this.mInfoText.length; i++) {
+                StringBuilder sb = this.mInfoText[i];
+                canvas.drawText(sb, 0, sb.length(), (float) leftMargin, (float) (this.mInfoTextOffset + (this.mInfoTextLineHeight * i)), this.mInfoPaint);
+            }
+            canvas.drawLines(this.mThresholdLines, this.mThresholdPaint);
+        }
+    }
+
+    private void computeInfoText() {
+        int dpc = this.mGestures.getDownPointers();
+        for (int i = 0; i < this.mInfoText.length; i++) {
+            StringBuilder sb = this.mInfoText[i];
+            sb.delete(0, sb.length());
+            if (i == 0) {
+                sb.append("th  ");
+                pad(sb, 9, this.mThresholdInfo.start);
+                sb.append("   ");
+                pad(sb, 9, this.mThresholdInfo.distance);
+                sb.append(' ');
+                pad(sb, MAX_POINTERS, (int) this.mThresholdInfo.elapsed);
+                sb.append("ms");
+            } else if (i - 1 < dpc) {
+                this.mGestures.getDownPointerInfo(i - 1, this.mDownPointerInfo);
+                sb.append('p');
+                sb.append(this.mDownPointerInfo.downPointerId);
+                sb.append(' ');
+                sb.append('(');
+                pad(sb, 4, (int) this.mDownPointerInfo.downX);
+                sb.append(',');
+                pad(sb, 4, (int) this.mDownPointerInfo.downY);
+                sb.append(')');
+                sb.append(' ');
+                sb.append('(');
+                pad(sb, 4, (int) (this.mDownPointerInfo.currentX - this.mDownPointerInfo.downX));
+                sb.append(',');
+                pad(sb, 4, (int) (this.mDownPointerInfo.currentY - this.mDownPointerInfo.downY));
+                sb.append(')');
+                sb.append(' ');
+                pad(sb, 4, (int) this.mDownPointerInfo.elapsedThreshold);
+                sb.append("ms");
+            }
+        }
+    }
+
+    private static void pad(StringBuilder sb, int len, int num) {
+        int n = num;
+        if (num < 0) {
+            n = Math.abs(num);
+            len--;
+        }
+        int nl = n > 0 ? ((int) Math.log10((double) n)) + 1 : 1;
+        while (true) {
+            int nl2 = nl + 1;
+            if (nl >= len) {
+                break;
+            }
+            sb.append(' ');
+            nl = nl2;
+        }
+        if (num < 0) {
+            sb.append('-');
+        }
+        sb.append(n);
+    }
+
+    private static List<float[]> allocatePoints() {
+        List<float[]> points = new ArrayList();
+        for (int i = 0; i < MAX_POINTERS; i++) {
+            points.add(new float[MAX_POINTS]);
+        }
+        return points;
+    }
+
+    private void onMotionEvent(MotionEvent ev) {
+        long evt;
+        int p;
+        Log.d(TAG, "EVENT " + ev);
+        if (ev.getActionMasked() == 0) {
+            clearScribbles();
+        }
+        int hs = ev.getHistorySize();
+        int ps = ev.getPointerCount();
+        for (int h = 0; h < hs; h++) {
+            evt = ev.getHistoricalEventTime(h);
+            for (p = 0; p < ps; p++) {
+                int pid = ev.getPointerId(p);
+                float hx = ev.getHistoricalX(p, h);
+                float hy = ev.getHistoricalY(p, h);
+                String str = TAG;
+                Object[] objArr = new Object[MAX_POINTERS];
+                objArr[0] = Long.valueOf(evt);
+                objArr[1] = Integer.valueOf(p);
+                objArr[2] = Integer.valueOf(pid);
+                objArr[3] = Float.valueOf(hx);
+                objArr[4] = Float.valueOf(hy);
+                Log.d(str, String.format("h.evt=%s p=%s pid=%s x=%s y=%s", objArr));
+                recordScribblePoint(pid, hx, hy);
+            }
+        }
+        evt = ev.getEventTime();
+        for (p = 0; p < ps; p++) {
+            int pid = ev.getPointerId(p);
+            float x = ev.getX(p);
+            float y = ev.getY(p);
+            String str = TAG;
+            Object[] objArr = new Object[MAX_POINTERS];
+            objArr[0] = Long.valueOf(evt);
+            objArr[1] = Integer.valueOf(p);
+            objArr[2] = Integer.valueOf(pid);
+            objArr[3] = Float.valueOf(x);
+            objArr[4] = Float.valueOf(y);
+            Log.d(str, String.format("c.evt=%s p=%s pid=%s x=%s y=%s", objArr));
+            recordScribblePoint(pid, x, y);
+        }
+        invalidate();
+    }
+
+    private void clearScribbles() {
+        for (int i = 0; i < this.mScribbleCounts.length; i++) {
+            this.mScribbleCounts[i] = 0;
+        }
+    }
+
+    private void recordScribblePoint(int pid, float x, float y) {
+        if (pid < MAX_POINTERS) {
+            float[] pts = (float[]) this.mScribblePoints.get(pid);
+            int oldCount = this.mScribbleCounts[pid];
+            if (oldCount + 4 >= MAX_POINTS) {
+                return;
+            }
+            int[] iArr;
+            if (oldCount == 0) {
+                pts[oldCount + 0] = x;
+                pts[oldCount + 1] = y;
+                iArr = this.mScribbleCounts;
+                iArr[pid] = iArr[pid] + 2;
+                return;
+            }
+            pts[oldCount + 0] = x;
+            pts[oldCount + 1] = y;
+            pts[oldCount + 2] = x;
+            pts[oldCount + 3] = y;
+            iArr = this.mScribbleCounts;
+            iArr[pid] = iArr[pid] + 4;
+        }
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/TransparencyToggleButton.java b/KBars/app/src/main/java/js/kbars/TransparencyToggleButton.java
new file mode 100644
index 0000000..ef14d85
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/TransparencyToggleButton.java
@@ -0,0 +1,43 @@
+package js.kbars;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.widget.Button;
+
+public final class TransparencyToggleButton extends Button {
+    private final String mDescription;
+    private boolean mTransparent;
+    private final int mWmFlag;
+
+    public TransparencyToggleButton(Context context, String description, int wmFlag) {
+        super(context);
+        this.mDescription = description;
+        this.mWmFlag = wmFlag;
+        setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                TransparencyToggleButton.this.toggle("clicked");
+            }
+        });
+        update();
+    }
+
+    private void toggle(String reason) {
+        Log.d(KBarsActivity.TAG, "toggle reason=" + reason);
+        this.mTransparent = !this.mTransparent;
+        update();
+    }
+
+    private void update() {
+        setText("Make " + this.mDescription + " " + (this.mTransparent ? "opaque" : "transparent"));
+        Window w = ((Activity) getContext()).getWindow();
+        if (this.mTransparent) {
+            w.addFlags(this.mWmFlag);
+        } else {
+            w.clearFlags(this.mWmFlag);
+        }
+    }
+}
diff --git a/KBars/app/src/main/java/js/kbars/Util.java b/KBars/app/src/main/java/js/kbars/Util.java
new file mode 100644
index 0000000..5cd4971
--- /dev/null
+++ b/KBars/app/src/main/java/js/kbars/Util.java
@@ -0,0 +1,42 @@
+package js.kbars;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.DisplayMetrics;
+import android.view.WindowManager;
+import java.lang.reflect.Field;
+
+public class Util {
+    public static String logTag(Class<?> c) {
+        return "kbars." + c.getSimpleName();
+    }
+
+    public static Object getField(Object obj, String fieldName) {
+        Class<?> c = obj.getClass();
+        try {
+            if (obj instanceof String) {
+                c = c.getClassLoader().loadClass((String) obj);
+                obj = null;
+            }
+            Field f = c.getDeclaredField(fieldName);
+            f.setAccessible(true);
+            return f.get(obj);
+        } catch (Throwable t) {
+            throw new RuntimeException(t);
+        }
+    }
+
+    public static int getDensityDpi(Context context) {
+        DisplayMetrics metrics = new DisplayMetrics();
+        ((WindowManager) context.getSystemService("window")).getDefaultDisplay().getMetrics(metrics);
+        return metrics.densityDpi;
+    }
+
+    public static String getVersionName(Context context) {
+        try {
+            return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/KBars/app/src/main/res/drawable-hdpi-v4/ic_launcher.png b/KBars/app/src/main/res/drawable-hdpi-v4/ic_launcher.png
new file mode 100644
index 0000000..b899b9e
--- /dev/null
+++ b/KBars/app/src/main/res/drawable-hdpi-v4/ic_launcher.png
Binary files differ
diff --git a/KBars/app/src/main/res/drawable-mdpi-v4/ic_launcher.png b/KBars/app/src/main/res/drawable-mdpi-v4/ic_launcher.png
new file mode 100644
index 0000000..d2b481b
--- /dev/null
+++ b/KBars/app/src/main/res/drawable-mdpi-v4/ic_launcher.png
Binary files differ
diff --git a/KBars/app/src/main/res/drawable-xhdpi-v4/ic_launcher.png b/KBars/app/src/main/res/drawable-xhdpi-v4/ic_launcher.png
new file mode 100644
index 0000000..4a26260
--- /dev/null
+++ b/KBars/app/src/main/res/drawable-xhdpi-v4/ic_launcher.png
Binary files differ
diff --git a/KBars/app/src/main/res/drawable-xxhdpi-v4/ic_launcher.png b/KBars/app/src/main/res/drawable-xxhdpi-v4/ic_launcher.png
new file mode 100644
index 0000000..cdae26b
--- /dev/null
+++ b/KBars/app/src/main/res/drawable-xxhdpi-v4/ic_launcher.png
Binary files differ
diff --git a/KBars/app/src/main/res/raw/clipcanvas.mp4 b/KBars/app/src/main/res/raw/clipcanvas.mp4
new file mode 100644
index 0000000..e41f4c7
--- /dev/null
+++ b/KBars/app/src/main/res/raw/clipcanvas.mp4
Binary files differ
diff --git a/KBars/app/src/main/res/values/strings.xml b/KBars/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..ea4b9c2
--- /dev/null
+++ b/KBars/app/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">(kbars)</string>
+    <string name="action_landscape">Landscape</string>
+    <string name="action_portrait">Portrait</string>
+    <string name="action_reverse_landscape">Reverse Landscape</string>
+    <string name="action_reverse_portrait">Reverse Portrait</string>
+</resources>
diff --git a/KBars/app/src/main/res/values/styles.xml b/KBars/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..fd4fc97
--- /dev/null
+++ b/KBars/app/src/main/res/values/styles.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <style name="AppBaseTheme">
+    </style>
+    <style name="AppTheme">
+    </style>
+</resources>
diff --git a/KBars/build.gradle b/KBars/build.gradle
new file mode 100644
index 0000000..2e0233b
--- /dev/null
+++ b/KBars/build.gradle
@@ -0,0 +1,28 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    ext.kotlin_version = '1.1.2-4'
+    repositories {
+        google()
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:3.0.0-alpha4'
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+        mavenCentral()
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}
diff --git a/KBars/settings.gradle b/KBars/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/KBars/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/PermissionApp/Android.mk b/PermissionApp/Android.mk
index 00170ca..1111750 100644
--- a/PermissionApp/Android.mk
+++ b/PermissionApp/Android.mk
@@ -2,12 +2,11 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := PermissionApp
 
-#LOCAL_CERTIFICATE := platform
-
 include $(BUILD_PACKAGE)
diff --git a/PermissionApp/AndroidManifest.xml b/PermissionApp/AndroidManifest.xml
index 8f35b34..c47fb6c 100644
--- a/PermissionApp/AndroidManifest.xml
+++ b/PermissionApp/AndroidManifest.xml
@@ -3,7 +3,6 @@
     android:versionCode="1"
     android:versionName="1.0">
 
-    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="22"/>
 
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
diff --git a/PermissionApp/src/foo/bar/permission/PermissionActivity.java b/PermissionApp/src/foo/bar/permission/PermissionActivity.java
index 529eccf..eb1d779 100644
--- a/PermissionApp/src/foo/bar/permission/PermissionActivity.java
+++ b/PermissionApp/src/foo/bar/permission/PermissionActivity.java
@@ -20,13 +20,9 @@
 import android.app.Activity;
 import android.app.LoaderManager;
 import android.bluetooth.BluetoothDevice;
-import android.companion.AssociationRequest;
-import android.companion.BluetoothDeviceFilter;
-import android.companion.BluetoothLEDeviceFilter;
 import android.companion.CompanionDeviceManager;
 import android.content.CursorLoader;
 import android.content.Intent;
-import android.content.IntentSender;
 import android.content.Loader;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
diff --git a/PrintApp/Android.mk b/PrintApp/Android.mk
index 81ab231..f09a7ea 100644
--- a/PrintApp/Android.mk
+++ b/PrintApp/Android.mk
@@ -2,6 +2,7 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/PrintService/Android.mk b/PrintService/Android.mk
index 6d4ff7f..8581d35 100644
--- a/PrintService/Android.mk
+++ b/PrintService/Android.mk
@@ -2,6 +2,7 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/SlicesApp/Android.mk b/SlicesApp/Android.mk
new file mode 100644
index 0000000..871aa4b
--- /dev/null
+++ b/SlicesApp/Android.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2017 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.
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := SlicesApp
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+	$(call all-Iaidl-files-under, src)
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
diff --git a/SlicesApp/AndroidManifest.xml b/SlicesApp/AndroidManifest.xml
new file mode 100644
index 0000000..87def87
--- /dev/null
+++ b/SlicesApp/AndroidManifest.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 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.
+     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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.experimental.slicesapp">
+
+    <uses-sdk
+        android:minSdkVersion="26" />
+
+    <uses-permission android:name="android.permission.BIND_SLICE" />
+
+    <application android:label="@string/app_label">
+        <activity android:name=".SlicesActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW_SLICE" />
+                <data android:scheme="content"
+                    android:host="com.android.experimental.slicesapp"
+                    android:pathPrefix="/main" />
+            </intent-filter>
+
+            <meta-data android:name="android.metadata.SLICE_URI"
+                android:value="content://com.android.experimental.slicesapp/main" />
+        </activity>
+
+        <provider android:name=".SlicesProvider"
+                android:authorities="com.android.experimental.slicesapp"
+                android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.EXAMPLE_SLICE_INTENT" />
+            </intent-filter>
+        </provider>
+
+        <receiver
+            android:name=".SlicesBroadcastReceiver"
+            android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.EXAMPLE_SLICE_ACTION"/>
+            </intent-filter>
+        </receiver>
+
+    </application>
+
+</manifest>
+
diff --git a/SlicesApp/res/drawable/ic_add.xml b/SlicesApp/res/drawable/ic_add.xml
new file mode 100644
index 0000000..56d3416
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_add.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2014 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.
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.0,13.0l-6.0,0.0l0.0,6.0l-2.0,0.0l0.0,-6.0L5.0,13.0l0.0,-2.0l6.0,0.0L11.0,5.0l2.0,0.0l0.0,6.0l6.0,0.0l0.0,2.0z"/>
+    <path
+        android:pathData="M0 0h24v24H0z"
+        android:fillColor="#00000000"/>
+</vector>
diff --git a/SlicesApp/res/drawable/ic_camera.xml b/SlicesApp/res/drawable/ic_camera.xml
new file mode 100644
index 0000000..1a2ef24
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_camera.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M 12 8.8 C 13.7673111995 8.8 15.2 10.2326888005 15.2 12 C 15.2 13.7673111995 13.7673111995 15.2 12 15.2 C 10.2326888005 15.2 8.8 13.7673111995 8.8 12 C 8.8 10.2326888005 10.2326888005 8.8 12 8.8 Z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1 .9 2 2 2h16c1.1 0 2-.9
+2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5
+5-2.24 5-5 5z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_car.xml b/SlicesApp/res/drawable/ic_car.xml
new file mode 100644
index 0000000..cdf0a6d
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_car.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M18.92 6.01C18.72 5.42 18.16 5 17.5 5h-11c-.66 0-1.21 .42 -1.42 1.01L3 12v8c0
+.55 .45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55 .45 1 1 1h1c.55 0 1-.45
+1-1v-8l-2.08-5.99zM6.5 16c-.83 0-1.5-.67-1.5-1.5S5.67 13 6.5 13s1.5 .67 1.5
+1.5S7.33 16 6.5 16zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5 .67 1.5
+1.5-.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_create.xml b/SlicesApp/res/drawable/ic_create.xml
new file mode 100644
index 0000000..2ab2fb7
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_create.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+</vector>
diff --git a/SlicesApp/res/drawable/ic_done.xml b/SlicesApp/res/drawable/ic_done.xml
new file mode 100644
index 0000000..5dc4039
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_done.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M0 0h24v24H0z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_down.xml b/SlicesApp/res/drawable/ic_down.xml
new file mode 100644
index 0000000..1399b16
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_down.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z" />
+    <path
+        android:pathData="M0-.75h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_home.xml b/SlicesApp/res/drawable/ic_home.xml
new file mode 100644
index 0000000..2332f3f
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_home.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_list.xml b/SlicesApp/res/drawable/ic_list.xml
new file mode 100644
index 0000000..6e8b482
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_list.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7
+7v2h14V7H7z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_next.xml b/SlicesApp/res/drawable/ic_next.xml
new file mode 100644
index 0000000..0b61487
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_next.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_pause.xml b/SlicesApp/res/drawable/ic_pause.xml
new file mode 100644
index 0000000..a581384
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_pause.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M6 19h4V5H6v14zm8-14v14h4V5h-4z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_photo.xml b/SlicesApp/res/drawable/ic_photo.xml
new file mode 100644
index 0000000..782c38c
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_photo.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1 .9 2 2 2h14c1.1 0 2-.9
+2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_play.xml b/SlicesApp/res/drawable/ic_play.xml
new file mode 100644
index 0000000..7457bac
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_play.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M8 5v14l11-7z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_prev.xml b/SlicesApp/res/drawable/ic_prev.xml
new file mode 100644
index 0000000..3bcca96
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_prev.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M6 6h2v12H6zm3.5 6l8.5 6V6z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_remove.xml b/SlicesApp/res/drawable/ic_remove.xml
new file mode 100644
index 0000000..76bd217
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_remove.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2014 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.
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.0,13.0L5.0,13.0l0.0,-2.0l14.0,0.0l0.0,2.0z"/>
+    <path
+        android:pathData="M0 0h24v24H0z"
+        android:fillColor="#00000000"/>
+</vector>
diff --git a/SlicesApp/res/drawable/ic_reply.xml b/SlicesApp/res/drawable/ic_reply.xml
new file mode 100644
index 0000000..af5643c
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_reply.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M10 9V5l-7 7 7 7v-4.1c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_share.xml b/SlicesApp/res/drawable/ic_share.xml
new file mode 100644
index 0000000..9942c06
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_share.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M0 0h24v24H0z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M18 16.08c-.76 0-1.44 .3 -1.96 .77 L8.91 12.7c.05-.23 .09 -.46 .09
+-.7s-.04-.47-.09-.7l7.05-4.11c.54 .5 1.25 .81 2.04 .81 1.66 0 3-1.34
+3-3s-1.34-3-3-3-3 1.34-3 3c0 .24 .04 .47 .09 .7L8.04 9.81C7.5 9.31 6.79 9 6
+9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05 .21 -.08
+.43 -.08 .65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31
+2.92-2.92s-1.31-2.92-2.92-2.92z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_slice.xml b/SlicesApp/res/drawable/ic_slice.xml
new file mode 100644
index 0000000..ce54529
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_slice.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M12,2C8.43,2 5.23,3.54 3.01,6L12,22l8.99,-16C18.78,3.55 15.57,2 12,2zM7,7c0,-1.1 0.9,-2 2,-2s2,0.9 2,2 -0.9,2 -2,2 -2,-0.9 -2,-2zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"
+        android:fillColor="#000000"/>
+</vector>
diff --git a/SlicesApp/res/drawable/ic_up.xml b/SlicesApp/res/drawable/ic_up.xml
new file mode 100644
index 0000000..dc67c88
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_up.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_video.xml b/SlicesApp/res/drawable/ic_video.xml
new file mode 100644
index 0000000..b19a75d
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_video.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M0 0h24v24H0z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55 .45 1 1 1h12c.55 0 1-.45
+1-1v-3.5l4 4v-11l-4 4z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_voice.xml b/SlicesApp/res/drawable/ic_voice.xml
new file mode 100644
index 0000000..e6c16f2
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_voice.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M7 24h2v-2H7v2zm5-11c1.66 0 2.99-1.34 2.99-3L15 4c0-1.66-1.34-3-3-3S9 2.34 9
+4v6c0 1.66 1.34 3 3 3zm-1 11h2v-2h-2v2zm4 0h2v-2h-2v2zm4-14h-1.7c0 3-2.54
+5.1-5.3 5.1S6.7 13 6.7 10H5c0 3.41 2.72 6.23 6 6.72V20h2v-3.28c3.28-.49 6-3.31
+6-6.72z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/ic_work.xml b/SlicesApp/res/drawable/ic_work.xml
new file mode 100644
index 0000000..2017fd2
--- /dev/null
+++ b/SlicesApp/res/drawable/ic_work.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M0 0h24v24H0z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M20 6h-4V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H4c-1.11 0-1.99 .89 -1.99 2L2
+19c0 1.11 .89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-6 0h-4V4h4v2z" />
+</vector>
\ No newline at end of file
diff --git a/SlicesApp/res/drawable/mady.jpg b/SlicesApp/res/drawable/mady.jpg
new file mode 100644
index 0000000..8b61f1b
--- /dev/null
+++ b/SlicesApp/res/drawable/mady.jpg
Binary files differ
diff --git a/SlicesApp/res/drawable/weather_1.png b/SlicesApp/res/drawable/weather_1.png
new file mode 100644
index 0000000..7c4034e
--- /dev/null
+++ b/SlicesApp/res/drawable/weather_1.png
Binary files differ
diff --git a/SlicesApp/res/drawable/weather_2.png b/SlicesApp/res/drawable/weather_2.png
new file mode 100644
index 0000000..f1b6672
--- /dev/null
+++ b/SlicesApp/res/drawable/weather_2.png
Binary files differ
diff --git a/SlicesApp/res/drawable/weather_3.png b/SlicesApp/res/drawable/weather_3.png
new file mode 100644
index 0000000..a5db683
--- /dev/null
+++ b/SlicesApp/res/drawable/weather_3.png
Binary files differ
diff --git a/SlicesApp/res/drawable/weather_4.png b/SlicesApp/res/drawable/weather_4.png
new file mode 100644
index 0000000..0b7f3b0
--- /dev/null
+++ b/SlicesApp/res/drawable/weather_4.png
Binary files differ
diff --git a/SlicesApp/res/layout/activity_layout.xml b/SlicesApp/res/layout/activity_layout.xml
new file mode 100644
index 0000000..7ad0cfe
--- /dev/null
+++ b/SlicesApp/res/layout/activity_layout.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+     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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:gravity="center">
+
+    <FrameLayout
+        android:id="@+id/slice_preview"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:paddingBottom="16dp"
+            android:gravity="center">
+
+            <Button
+                android:id="@+id/subtract"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="-"/>
+
+            <TextView android:id="@+id/summary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:padding="48dp"
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+
+            <Button
+                android:id="@+id/add"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="+"/>
+
+        </LinearLayout>
+
+        <Button
+            android:id="@+id/auth"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Auth-everything"/>
+
+        <Spinner
+            android:id="@+id/spinner"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <CheckBox
+            android:id="@+id/header"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Show header" />
+
+        <CheckBox
+            android:id="@+id/sub_header"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Show sub header" />
+
+        <CheckBox
+            android:id="@+id/show_action_row"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Show action row" />
+
+    </LinearLayout>
+</LinearLayout>
+
diff --git a/SlicesApp/res/values/strings.xml b/SlicesApp/res/values/strings.xml
new file mode 100644
index 0000000..ff364c5
--- /dev/null
+++ b/SlicesApp/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+     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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label">Slices Client</string>
+</resources>
diff --git a/SlicesApp/src/com/android/experimental/slicesapp/SlicesActivity.java b/SlicesApp/src/com/android/experimental/slicesapp/SlicesActivity.java
new file mode 100644
index 0000000..9dc4049
--- /dev/null
+++ b/SlicesApp/src/com/android/experimental/slicesapp/SlicesActivity.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 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. 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.android.experimental.slicesapp;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.Spinner;
+import android.widget.TextView;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SlicesActivity extends Activity {
+
+    private static final String TAG = "SlicesActivity";
+
+    private int mState;
+    private ViewGroup mSlicePreviewFrame;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        SharedPreferences state = getSharedPreferences("slice", 0);
+        mState = 0;
+        if (state != null) {
+            mState = Integer.parseInt(state.getString(getUri().toString(), "0"));
+        }
+        setContentView(R.layout.activity_layout);
+        mSlicePreviewFrame = (ViewGroup) findViewById(R.id.slice_preview);
+        findViewById(R.id.subtract).setOnClickListener(v -> {
+            mState--;
+            state.edit().putString(getUri().toString(), String.valueOf(mState)).commit();
+            updateState();
+            getContentResolver().notifyChange(getUri(), null);
+        });
+        findViewById(R.id.add).setOnClickListener(v -> {
+            mState++;
+            state.edit().putString(getUri().toString(), String.valueOf(mState)).commit();
+            updateState();
+            getContentResolver().notifyChange(getUri(), null);
+        });
+        findViewById(R.id.auth).setOnClickListener(v -> {
+            List<ApplicationInfo> packages = getPackageManager().getInstalledApplications(0);
+            packages.forEach(info -> grantUriPermission(info.packageName, getUri(),
+                    Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION));
+        });
+
+        Spinner spinner = findViewById(R.id.spinner);
+        List<String> list = new ArrayList<>();
+        list.add("Default");
+        list.add("Single-line");
+        list.add("Single-line action");
+        list.add("Two-line");
+        list.add("Two-line action");
+        list.add("Weather");
+        list.add("Messaging");
+        list.add("Keep actions");
+        list.add("Maps multi");
+        list.add("Settings");
+        list.add("Settings content");
+        ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
+                android.R.layout.simple_spinner_item, list);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        spinner.setAdapter(adapter);
+        spinner.setSelection(list.indexOf(state.getString("slice_type", "Default")));
+        spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                state.edit().putString("slice_type", list.get(position)).commit();
+                getContentResolver().notifyChange(getUri(), null);
+                updateSlice(getUri());
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+            }
+        });
+
+        bindCheckbox(R.id.header, state, "show_header");
+        bindCheckbox(R.id.sub_header, state, "show_sub_header");
+        bindCheckbox(R.id.show_action_row, state, "show_action_row");
+
+        updateState();
+    }
+
+    private void bindCheckbox(int id, SharedPreferences state, String key) {
+        CheckBox checkbox = findViewById(id);
+        checkbox.setChecked(state.getBoolean(key, false));
+        checkbox.setOnCheckedChangeListener((v, isChecked) -> {
+            state.edit().putBoolean(key, isChecked).commit();
+            getContentResolver().notifyChange(getUri(), null);
+            updateSlice(getUri());
+        });
+    }
+
+    private void updateState() {
+        ((TextView) findViewById(R.id.summary)).setText(String.valueOf(mState));
+    }
+
+    public Uri getUri() {
+        return new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(getPackageName())
+                .appendPath("main").build();
+    }
+
+    private void updateSlice(Uri uri) {
+    }
+}
diff --git a/SlicesApp/src/com/android/experimental/slicesapp/SlicesBroadcastReceiver.java b/SlicesApp/src/com/android/experimental/slicesapp/SlicesBroadcastReceiver.java
new file mode 100644
index 0000000..26adaa7
--- /dev/null
+++ b/SlicesApp/src/com/android/experimental/slicesapp/SlicesBroadcastReceiver.java
@@ -0,0 +1,29 @@
+package com.android.experimental.slicesapp;
+
+import android.app.slice.Slice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+public class SlicesBroadcastReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if (SlicesProvider.SLICE_ACTION.equals(action)) {
+            // A slice was clicked and we've got an action to handle
+            Bundle bundle = intent.getExtras();
+            String message = "";
+            boolean newState = false;
+            if (bundle != null) {
+                message = bundle.getString(SlicesProvider.INTENT_ACTION_EXTRA);
+                newState = bundle.getBoolean(Slice.EXTRA_TOGGLE_STATE);
+            }
+            String text = message + " new state: " + newState;
+            Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
+        }
+    }
+}
diff --git a/SlicesApp/src/com/android/experimental/slicesapp/SlicesProvider.java b/SlicesApp/src/com/android/experimental/slicesapp/SlicesProvider.java
new file mode 100644
index 0000000..125bbb7
--- /dev/null
+++ b/SlicesApp/src/com/android/experimental/slicesapp/SlicesProvider.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2017 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. 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.android.experimental.slicesapp;
+
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.app.slice.Slice;
+import android.app.slice.Slice.Builder;
+import android.app.slice.SliceProvider;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import java.util.function.Consumer;
+
+
+public class SlicesProvider extends SliceProvider {
+
+    private static final String TAG = "SliceProvider";
+    public static final String SLICE_INTENT = "android.intent.action.EXAMPLE_SLICE_INTENT";
+    public static final String SLICE_ACTION = "android.intent.action.EXAMPLE_SLICE_ACTION";
+    public static final String INTENT_ACTION_EXTRA = "android.intent.slicesapp.INTENT_ACTION_EXTRA";
+
+    private final int NUM_LIST_ITEMS = 10;
+
+    private SharedPreferences mSharedPrefs;
+
+    @Override
+    public boolean onCreate() {
+        mSharedPrefs = getContext().getSharedPreferences("slice", 0);
+        return true;
+    }
+
+    private Uri getIntentUri() {
+        return new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(getContext().getPackageName())
+                .appendPath("main").appendPath("intent")
+                .build();
+    }
+
+    //@Override
+    public Uri onMapIntentToUri(Intent intent) {
+        if (intent.getAction().equals(SLICE_INTENT)) {
+            return getIntentUri();
+        }
+        return null;//super.onMapIntentToUri(intent);
+    }
+
+    /**
+     * Overriding onBindSlice will generate one Slice for all modes.
+     * @param sliceUri
+     */
+    @Override
+    public Slice onBindSlice(Uri sliceUri) {
+        Log.w(TAG, "onBindSlice uri: " + sliceUri);
+        String type = mSharedPrefs.getString("slice_type", "Default");
+        if ("Default".equals(type)) {
+            return null;
+        }
+        Slice.Builder b = new Builder(sliceUri);
+        if (mSharedPrefs.getBoolean("show_header", false)) {
+            b.addText("Header", null, Slice.HINT_TITLE);
+            if (mSharedPrefs.getBoolean("show_sub_header", false)) {
+                b.addText("Sub-header", null);
+            }
+        }
+        if (sliceUri.equals(getIntentUri())) {
+            type = "Intent";
+        }
+        switch (type) {
+            case "Shortcut":
+                b.addColor(Color.CYAN, null).addIcon(
+                        Icon.createWithResource(getContext(), R.drawable.mady),
+                        null,
+                        Slice.HINT_LARGE);
+                break;
+            case "Single-line":
+                b.addSubSlice(makeList(new Slice.Builder(b), this::makeSingleLine,
+                        this::addIcon));
+                addPrimaryAction(b);
+                break;
+            case "Single-line action":
+                b.addSubSlice(makeList(new Slice.Builder(b), this::makeSingleLine,
+                        this::addAltActions));
+                addPrimaryAction(b);
+                break;
+            case "Two-line":
+                b.addSubSlice(makeList(new Slice.Builder(b), this::makeTwoLine,
+                        this::addIcon));
+                addPrimaryAction(b);
+                break;
+            case "Two-line action":
+                b.addSubSlice(makeList(new Slice.Builder(b), this::makeTwoLine,
+                        this::addAltActions));
+                addPrimaryAction(b);
+                break;
+            case "Weather":
+                b.addSubSlice(createWeather(new Slice.Builder(b).addHints(Slice.HINT_HORIZONTAL)));
+                break;
+            case "Messaging":
+                createConversation(b);
+                break;
+            case "Keep actions":
+                b.addSubSlice(createKeepNote(new Slice.Builder(b)));
+                break;
+            case "Maps multi":
+                b.addSubSlice(createMapsMulti(new Slice.Builder(b)
+                        .addHints(Slice.HINT_HORIZONTAL)));
+                break;
+            case "Intent":
+                b.addSubSlice(createIntentSlice(new Slice.Builder(b)
+                        .addHints(Slice.HINT_HORIZONTAL)));
+                break;
+            case "Settings":
+                createSettingsSlice(b);
+                break;
+            case "Settings content":
+                createSettingsContentSlice(b);
+                break;
+        }
+        if (mSharedPrefs.getBoolean("show_action_row", false)) {
+            Intent intent = new Intent(getContext(), SlicesActivity.class);
+            PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 0, intent, 0);
+            b.addSubSlice(new Slice.Builder(b).addHints(Slice.HINT_ACTIONS)
+                    .addAction(pendingIntent, new Slice.Builder(b)
+                            .addText("Action1", null)
+                            .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_add),
+                                    null)
+                            .build())
+                    .addAction(pendingIntent, new Slice.Builder(b)
+                            .addText("Action2", null)
+                            .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_remove),
+                                    null)
+                            .build())
+                    .addAction(pendingIntent, new Slice.Builder(b)
+                            .addText("Action3", null)
+                            .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_add),
+                                    null)
+                            .build())
+                    .build());
+        }
+        return b.build();
+    }
+
+    private Slice createWeather(Builder grid) {
+        grid.addSubSlice(new Slice.Builder(grid)
+                .addIcon(Icon.createWithResource(getContext(), R.drawable.weather_1),
+                        Slice.HINT_LARGE)
+                .addText("MON", null)
+                .addText("69\u00B0", null, Slice.HINT_LARGE).build());
+        grid.addSubSlice(new Slice.Builder(grid)
+                .addIcon(Icon.createWithResource(getContext(), R.drawable.weather_2),
+                        Slice.HINT_LARGE)
+                .addText("TUE", null)
+                .addText("71\u00B0", null, Slice.HINT_LARGE).build());
+        grid.addSubSlice(new Slice.Builder(grid)
+                .addIcon(Icon.createWithResource(getContext(), R.drawable.weather_3),
+                        Slice.HINT_LARGE)
+                .addText("WED", null)
+                .addText("76\u00B0", null, Slice.HINT_LARGE).build());
+        grid.addSubSlice(new Slice.Builder(grid)
+                .addIcon(Icon.createWithResource(getContext(), R.drawable.weather_4),
+                        Slice.HINT_LARGE)
+                .addText("THU", null)
+                .addText("69\u00B0", null, Slice.HINT_LARGE).build());
+        grid.addSubSlice(new Slice.Builder(grid)
+                .addIcon(Icon.createWithResource(getContext(), R.drawable.weather_2),
+                        Slice.HINT_LARGE)
+                .addText("FRI", null)
+                .addText("71\u00B0", null, Slice.HINT_LARGE).build());
+        return grid.build();
+    }
+
+    private Slice createConversation(Builder b2) {
+        b2.addHints(Slice.HINT_LIST);
+        b2.addSubSlice(new Slice.Builder(b2)
+                .addText("yo home \uD83C\uDF55, I emailed you the info", null)
+                .addTimestamp(System.currentTimeMillis() - 20 * DateUtils.MINUTE_IN_MILLIS, null)
+                .addIcon(Icon.createWithResource(getContext(), R.drawable.mady),
+                        Slice.SUBTYPE_SOURCE,
+                        Slice.HINT_TITLE, Slice.HINT_LARGE)
+                .build(), Slice.SUBTYPE_MESSAGE);
+        b2.addSubSlice(new Builder(b2)
+                .addText("just bought my tickets", null)
+                .addTimestamp(System.currentTimeMillis() - 10 * DateUtils.MINUTE_IN_MILLIS, null)
+                .build(), Slice.SUBTYPE_MESSAGE);
+        b2.addSubSlice(new Builder(b2)
+                .addText("yay! can't wait for getContext() weekend!\n"
+                        + "\uD83D\uDE00", null)
+                .addTimestamp(System.currentTimeMillis() - 5 * DateUtils.MINUTE_IN_MILLIS, null)
+                .addIcon(Icon.createWithResource(getContext(), R.drawable.mady),
+                        Slice.SUBTYPE_SOURCE,
+                        Slice.HINT_LARGE)
+                .build(), Slice.SUBTYPE_MESSAGE);
+        RemoteInput ri = new RemoteInput.Builder("someKey").setLabel("someLabel")
+                .setAllowFreeFormInput(true).build();
+        b2.addRemoteInput(ri, null);
+        return b2.build();
+    }
+
+    private Slice addIcon(Builder b) {
+        b.addIcon(Icon.createWithResource(getContext(), R.drawable.ic_add), null);
+        return b.build();
+    }
+
+    private void addAltActions(Builder builder) {
+        Intent intent = new Intent(getContext(), SlicesActivity.class);
+        PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 0, intent, 0);
+        builder.addSubSlice(new Slice.Builder(builder).addHints(Slice.HINT_ACTIONS)
+                .addAction(pendingIntent, new Slice.Builder(builder)
+                        .addText("Alt1", null)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_add), null)
+                        .build())
+                .addAction(pendingIntent, new Slice.Builder(builder)
+                        .addText("Alt2", null)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_remove), null)
+                        .build())
+                .build());
+    }
+
+    private void makeSingleLine(Builder b) {
+        b.addText("Single-line list item text", null, Slice.HINT_TITLE);
+    }
+
+    private void makeTwoLine(Builder b) {
+        b.addText("Two-line list item text", null, Slice.HINT_TITLE);
+        b.addText("Secondary text", null);
+    }
+
+    private void addPrimaryAction(Builder b) {
+        Intent intent = new Intent(getContext(), SlicesActivity.class);
+        PendingIntent pi = PendingIntent.getActivity(getContext(), 0, intent, 0);
+        b.addSubSlice(new Slice.Builder(b).addAction(pi,
+                new Slice.Builder(b).addColor(0xFFFF5722, null)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_slice),
+                                Slice.HINT_TITLE)
+                        .addText("Slice App", null, Slice.HINT_TITLE)
+                        .build()).addHints(Slice.HINT_HIDDEN, Slice.HINT_TITLE).build());
+    }
+
+    private Slice makeList(Builder list, Consumer<Builder> lineCreator,
+            Consumer<Builder> lineHandler) {
+        list.addHints(Slice.HINT_LIST);
+        for (int i = 0; i < NUM_LIST_ITEMS; i++) {
+            Builder b = new Builder(list);
+            lineCreator.accept(b);
+            lineHandler.accept(b);
+            list.addSubSlice(b.build());
+        }
+        return list.build();
+    }
+
+    private Slice createKeepNote(Builder b) {
+        Intent intent = new Intent(getContext(), SlicesActivity.class);
+        PendingIntent pi = PendingIntent.getActivity(getContext(), 0, intent, 0);
+        RemoteInput ri = new RemoteInput.Builder("someKey").setLabel("someLabel")
+                .setAllowFreeFormInput(true).build();
+        return b.addText("Create new note", null, Slice.HINT_TITLE).addText("with keep", null)
+                .addColor(0xffffc107, null)
+                .addAction(pi, new Slice.Builder(b)
+                        .addText("List", null)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_list), null)
+                        .build())
+                .addAction(pi, new Slice.Builder(b)
+                        .addText("Voice note", null)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_voice), null)
+                        .build())
+                .addAction(pi, new Slice.Builder(b)
+                        .addText("Camera", null)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_camera), null)
+                        .build())
+                .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_create), null)
+                .addRemoteInput(ri, null)
+                .build();
+    }
+
+    private Slice createMapsMulti(Builder b) {
+        Intent intent = new Intent(getContext(), SlicesActivity.class);
+        PendingIntent pi = PendingIntent.getActivity(getContext(), 0, intent, 0);
+        b.addHints(Slice.HINT_HORIZONTAL, Slice.HINT_LIST);
+
+        b.addSubSlice(new Slice.Builder(b)
+                .addAction(pi, new Slice.Builder(b)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_home), null)
+                        .build())
+                .addText("Home", null, Slice.HINT_LARGE)
+                .addText("25 min", null).build());
+        b.addSubSlice(new Slice.Builder(b)
+                .addAction(pi, new Slice.Builder(b)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_work), null)
+                        .build())
+                .addText("Work", null, Slice.HINT_LARGE)
+                .addText("1 hour 23 min", null).build());
+        b.addSubSlice(new Slice.Builder(b)
+                .addAction(pi, new Slice.Builder(b)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_car), null)
+                        .build())
+                .addText("Mom's", null, Slice.HINT_LARGE)
+                .addText("37 min", null).build());
+        b.addColor(0xff0B8043, null);
+        return b.build();
+    }
+
+    private Slice createIntentSlice(Builder b) {
+        Intent intent = new Intent(getContext(), SlicesActivity.class);
+
+        PendingIntent pi = PendingIntent.getActivity(getContext(), 0, intent, 0);
+
+        b.addHints(Slice.HINT_LIST).addColor(0xff0B8043, null);
+        b.addSubSlice(new Slice.Builder(b)
+                .addAction(pi, new Slice.Builder(b)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_next), null)
+                        .build())
+                .addText("Next", null, Slice.HINT_LARGE).build());
+        b.addSubSlice(new Slice.Builder(b)
+                .addAction(pi, new Slice.Builder(b)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_play), null)
+                        .build())
+                .addText("Play", null, Slice.HINT_LARGE).build());
+        b.addSubSlice(new Slice.Builder(b)
+                .addAction(pi, new Slice.Builder(b)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.ic_prev), null)
+                        .build())
+                .addText("Previous", null, Slice.HINT_LARGE).build());
+        return b.build();
+    }
+
+    private Slice.Builder createSettingsSlice(Builder b) {
+        b.addSubSlice(new Slice.Builder(b)
+                .addAction(getIntent("toggled"), new Slice.Builder(b)
+                        .addText("Wi-fi", null)
+                        .addText("GoogleGuest", null)
+                        .addHints(Slice.HINT_TOGGLE, Slice.HINT_SELECTED)
+                        .build())
+                .build());
+        return b;
+    }
+
+    private Slice.Builder createSettingsContentSlice(Builder b) {
+        b.addSubSlice(new Slice.Builder(b)
+                .addAction(getIntent("main content"),
+                        new Slice.Builder(b)
+                                .addText("Wi-fi", null)
+                                .addText("GoogleGuest", null)
+                                .build())
+                .addAction(getIntent("toggled"),
+                        new Slice.Builder(b)
+                                .addHints(Slice.HINT_TOGGLE, Slice.HINT_SELECTED)
+                                .build())
+                .build());
+        return b;
+    }
+
+    private PendingIntent getIntent(String message) {
+        Intent intent = new Intent(SLICE_ACTION);
+        intent.setClass(getContext(), SlicesBroadcastReceiver.class);
+        intent.putExtra(INTENT_ACTION_EXTRA, message);
+        PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, intent,
+                PendingIntent.FLAG_CANCEL_CURRENT);
+        return pi;
+    }
+}
diff --git a/TestBack/src/foo/bar/testback/AccessibilityFocusManager.java b/TestBack/src/foo/bar/testback/AccessibilityFocusManager.java
new file mode 100644
index 0000000..c6b91ae
--- /dev/null
+++ b/TestBack/src/foo/bar/testback/AccessibilityFocusManager.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017 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.
+ * 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 foo.bar.testback;
+
+import static foo.bar.testback.AccessibilityNodeInfoUtils.findParent;
+
+import android.text.TextUtils;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * Helper class to manage accessibility focus
+ */
+public class AccessibilityFocusManager {
+    private static final AccessibilityAction[] IGNORED_ACTIONS_FOR_A11Y_FOCUS = {
+            AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS,
+            AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
+            AccessibilityAction.ACTION_SELECT,
+            AccessibilityAction.ACTION_CLEAR_SELECTION,
+            AccessibilityAction.ACTION_SHOW_ON_SCREEN
+    };
+
+    public static final boolean canTakeAccessibilityFocus(AccessibilityNodeInfo nodeInfo) {
+        if (nodeInfo.isFocusable() || nodeInfo.isScreenReaderFocusable()) {
+            return true;
+        }
+
+        List<AccessibilityAction> actions = new ArrayList<>(nodeInfo.getActionList());
+        actions.removeAll(Arrays.asList(IGNORED_ACTIONS_FOR_A11Y_FOCUS));
+
+        // Nodes with relevant actions are always focusable
+        if (!actions.isEmpty()) {
+            return true;
+        }
+
+        // If a parent is specifically marked focusable, then this node should not be.
+        if (findParent(nodeInfo,
+                (parent) -> parent.isFocusable() || parent.isScreenReaderFocusable()) != null) {
+            return false;
+        }
+
+        return !TextUtils.isEmpty(nodeInfo.getText())
+                || !TextUtils.isEmpty(nodeInfo.getContentDescription());
+    };
+}
diff --git a/TestBack/src/foo/bar/testback/AccessibilityNodeInfoUtils.java b/TestBack/src/foo/bar/testback/AccessibilityNodeInfoUtils.java
new file mode 100644
index 0000000..f4e9398
--- /dev/null
+++ b/TestBack/src/foo/bar/testback/AccessibilityNodeInfoUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017 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.
+ * 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 foo.bar.testback;
+
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import java.util.function.Predicate;
+
+/**
+ * Utility class for working with AccessibilityNodeInfo
+ */
+public class AccessibilityNodeInfoUtils {
+    public static AccessibilityNodeInfo findParent(
+            AccessibilityNodeInfo start, Predicate<AccessibilityNodeInfo> condition) {
+        AccessibilityNodeInfo parent = start.getParent();
+        if ((parent == null) || (condition.test(parent))) {
+            return parent;
+        }
+
+        return findParent(parent, condition);
+    }
+
+    public static AccessibilityNodeInfo findChildDfs(
+            AccessibilityNodeInfo start, Predicate<AccessibilityNodeInfo> condition) {
+        int numChildren = start.getChildCount();
+        for (int i = 0; i < numChildren; i++) {
+            AccessibilityNodeInfo child = start.getChild(i);
+            if (child != null) {
+                if (condition.test(child)) {
+                    return child;
+                }
+                AccessibilityNodeInfo childResult = findChildDfs(child, condition);
+                if (childResult != null) {
+                    return childResult;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/TestBack/src/foo/bar/testback/TestBackService.java b/TestBack/src/foo/bar/testback/TestBackService.java
index 53db125..bd5329b 100644
--- a/TestBack/src/foo/bar/testback/TestBackService.java
+++ b/TestBack/src/foo/bar/testback/TestBackService.java
@@ -1,8 +1,30 @@
+/*
+ * Copyright 2017 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.
+ * 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 foo.bar.testback;
 
+import static foo.bar.testback.AccessibilityNodeInfoUtils.findChildDfs;
+import static foo.bar.testback.AccessibilityNodeInfoUtils.findParent;
+
+import static foo.bar.testback.AccessibilityFocusManager.CAN_TAKE_A11Y_FOCUS;
+
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.content.Context;
+import android.graphics.Rect;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.WindowManager;
@@ -13,6 +35,7 @@
 
 import java.util.List;
 import java.util.Set;
+import java.util.function.Predicate;
 
 public class TestBackService extends AccessibilityService {
 
@@ -20,6 +43,11 @@
 
     private Button mButton;
 
+    int mRowIndexOfA11yFocus = -1;
+    int mColIndexOfA11yFocus = -1;
+    AccessibilityNodeInfo mCollectionWithAccessibiltyFocus;
+    AccessibilityNodeInfo mCurrentA11yFocus;
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -29,53 +57,61 @@
 
     @Override
     public void onAccessibilityEvent(AccessibilityEvent event) {
-//        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
-//            Log.i(LOG_TAG, event.getText().toString());
-//            //dumpWindows();
-//        }
-        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER) {
-//            Log.i(LOG_TAG, "Click event.isChecked()=" + event.isChecked()
-//                    + ((event.getSource() != null) ? " node.isChecked()="
-//                    + event.getSource().isChecked() : " node=null"));
-
-            AccessibilityNodeInfo source = event.getSource();
-            dumpWindow(source);
-//            AccessibilityNodeInfo node = event.getSource();
-//            if (node != null) {
-//                node.refresh();
-//                Log.i(LOG_TAG, "Clicked: " + node.getClassName() + " clicked:" + node.isChecked());
-//            }
+        int eventType = event.getEventType();
+        AccessibilityNodeInfo source = event.getSource();
+        if (eventType == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER) {
+            if (source != null) {
+                AccessibilityNodeInfo focusNode =
+                        (CAN_TAKE_A11Y_FOCUS.test(source)) ? source : findParent(
+                                source, AccessibilityFocusManager::canTakeAccessibilityFocus);
+                if (focusNode != null) {
+                    focusNode.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+                }
+            }
+            return;
         }
-    }
-
-    @Override
-    protected boolean onGesture(int gestureId) {
-        switch (gestureId) {
-            case AccessibilityService.GESTURE_SWIPE_DOWN: {
-                showAccessibilityOverlay();
-            } break;
-            case AccessibilityService.GESTURE_SWIPE_UP: {
-                hideAccessibilityOverlay();
-            } break;
+        if (eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
+            mCurrentA11yFocus = source;
+            AccessibilityNodeInfo.CollectionItemInfo itemInfo = source.getCollectionItemInfo();
+            if (itemInfo == null) {
+                mRowIndexOfA11yFocus = -1;
+                mColIndexOfA11yFocus = -1;
+                mCollectionWithAccessibiltyFocus = null;
+            } else {
+                mRowIndexOfA11yFocus = itemInfo.getRowIndex();
+                mColIndexOfA11yFocus = itemInfo.getColumnIndex();
+                mCollectionWithAccessibiltyFocus = findParent(source,
+                        (nodeInfo) -> nodeInfo.getCollectionInfo() != null);
+            }
+            Rect bounds = new Rect();
+            source.getBoundsInScreen(bounds);
         }
-        return super.onGesture(gestureId);
-    }
 
-    private void showAccessibilityOverlay() {
-        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
-        params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
-                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-        params.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+        if (eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED) {
+            mCurrentA11yFocus = null;
+            return;
+        }
 
-        WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
-        windowManager.addView(mButton, params);
-    }
+        if (eventType == AccessibilityEvent.TYPE_WINDOWS_CHANGED) {
+            mRowIndexOfA11yFocus = -1;
+            mColIndexOfA11yFocus = -1;
+            mCollectionWithAccessibiltyFocus = null;
+        }
 
-    private void hideAccessibilityOverlay() {
-        WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
-        windowManager.removeView(mButton);
+        if ((mCurrentA11yFocus == null) && (mCollectionWithAccessibiltyFocus != null)) {
+            // Look for a node to re-focus
+            AccessibilityNodeInfo focusRestoreTarget = findChildDfs(
+                    mCollectionWithAccessibiltyFocus, (nodeInfo) -> {
+                        AccessibilityNodeInfo.CollectionItemInfo collectionItemInfo =
+                                nodeInfo.getCollectionItemInfo();
+                        return (collectionItemInfo != null)
+                                && (collectionItemInfo.getRowIndex() == mRowIndexOfA11yFocus)
+                                && (collectionItemInfo.getColumnIndex() == mColIndexOfA11yFocus);
+                    });
+            if (focusRestoreTarget != null) {
+                focusRestoreTarget.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+            }
+        }
     }
 
     private void dumpWindows() {