Update sdk.atree and samples/browseable for lastest samples release
Synced to developers/samples/android commit 97b2cfe5ba6d8fa8daaf3273141b321b5fe9e910. Change-Id: I360cfa147e71dd519b841df41b4e878f86b9b27b
@@ -280,9 +280,7 @@ developers/build/prebuilts/gradle/CardReader sam
|
||||
developers/build/prebuilts/gradle/BatchStepSensor samples/${PLATFORM_NAME}/sensors/BatchStepSensor
|
||||
developers/build/prebuilts/gradle/DisplayingBitmaps samples/${PLATFORM_NAME}/ui/DisplayingBitmaps
|
||||
developers/build/prebuilts/gradle/BasicTransition samples/${PLATFORM_NAME}/ui/BasicTransition
|
||||
developers/build/prebuilts/gradle/AdapterTransition samples/${PLATFORM_NAME}/ui/AdapterTransition
|
||||
developers/build/prebuilts/gradle/CustomTransition samples/${PLATFORM_NAME}/ui/CustomTransition
|
||||
developers/build/prebuilts/gradle/FragmentTransition samples/${PLATFORM_NAME}/ui/FragmentTransition
|
||||
developers/build/prebuilts/gradle/SwipeRefreshLayoutBasic samples/${PLATFORM_NAME}/ui/SwipeRefreshLayoutBasic
|
||||
developers/build/prebuilts/gradle/SwipeRefreshListFragment samples/${PLATFORM_NAME}/ui/SwipeRefreshListFragment
|
||||
developers/build/prebuilts/gradle/SwipeRefreshMultipleViews samples/${PLATFORM_NAME}/ui/SwipeRefreshMultipleViews
|
||||
@@ -314,6 +312,8 @@ developers/build/prebuilts/gradle/PermissionRequest sam
|
||||
developers/build/prebuilts/gradle/DirectorySelection samples/${PLATFORM_NAME}/content/documentsUi/DirectorySelection
|
||||
developers/build/prebuilts/gradle/AppUsageStatistics samples/${PLATFORM_NAME}/system/AppUsageStatistics
|
||||
developers/build/prebuilts/gradle/ScreenCapture samples/${PLATFORM_NAME}/media/ScreenCapture
|
||||
developers/build/prebuilts/gradle/NfcProvisioning samples/${PLATFORM_NAME}/nfc/NfcProvisioning
|
||||
developers/build/prebuilts/gradle/DeviceOwner samples/${PLATFORM_NAME}/admin/DeviceOwner
|
||||
|
||||
developers/build/prebuilts/androidtv samples/${PLATFORM_NAME}/androidtv
|
||||
|
||||
@@ -322,7 +322,6 @@ developers/build/prebuilts/gradle/AgendaData samples/${PLATFO
|
||||
developers/build/prebuilts/gradle/DataLayer samples/${PLATFORM_NAME}/wearable/DataLayer
|
||||
developers/build/prebuilts/gradle/DelayedConfirmation samples/${PLATFORM_NAME}/wearable/DelayedConfirmation
|
||||
developers/build/prebuilts/gradle/ElizaChat samples/${PLATFORM_NAME}/wearable/ElizaChat
|
||||
developers/build/prebuilts/gradle/EmbeddedApp samples/${PLATFORM_NAME}/wearable/EmbeddedApp
|
||||
developers/build/prebuilts/gradle/FindMyPhone samples/${PLATFORM_NAME}/wearable/FindMyPhone
|
||||
developers/build/prebuilts/gradle/Flashlight samples/${PLATFORM_NAME}/wearable/Flashlight
|
||||
developers/build/prebuilts/gradle/Geofencing samples/${PLATFORM_NAME}/wearable/Geofencing
|
||||
@@ -337,6 +336,7 @@ developers/build/prebuilts/gradle/SynchronizedNotifications samples/${PLATFO
|
||||
developers/build/prebuilts/gradle/Timer samples/${PLATFORM_NAME}/wearable/Timer
|
||||
developers/build/prebuilts/gradle/WatchFace samples/${PLATFORM_NAME}/wearable/WatchFace
|
||||
developers/build/prebuilts/gradle/WatchViewStub samples/${PLATFORM_NAME}/wearable/WatchViewStub
|
||||
developers/build/prebuilts/gradle/XYZTouristAttractions samples/${PLATFORM_NAME}/wearable/XYZTouristAttractions
|
||||
|
||||
# Old sample tree
|
||||
development/samples/AccelerometerPlay samples/${PLATFORM_NAME}/legacy/AccelerometerPlay
|
||||
@@ -498,4 +498,3 @@ ${OUT_DIR}/target/common/obj/PACKAGING/android-support-v17-leanback_intermediate
|
||||
##############################################################################
|
||||
framework/layoutlib-tests.jar tests/libtests/layoutlib-tests.jar
|
||||
system/app/EmulatorSmokeTests/EmulatorSmokeTests.apk tests/emulator-test-apps/EmulatorSmokeTests.apk
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 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.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.android.adaptertransition"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
|
||||
<!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle -->
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name="com.example.android.adaptertransition.MainActivity"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -1,9 +0,0 @@
|
||||
page.tags="AdapterTransition"
|
||||
sample.group=UI
|
||||
@jd:body
|
||||
|
||||
<p>
|
||||
|
||||
Transition cannot be directly applied to AdapterViews. In this sample, we demonstrate how to create an overlay layout and run a Transition on it. Press the action bar button to toggle between ListView and GridView.
|
||||
|
||||
</p>
|
||||
|
Before Width: | Height: | Size: 412 B |
|
Before Width: | Height: | Size: 496 B |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 241 B |
|
Before Width: | Height: | Size: 364 B |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 407 B |
|
Before Width: | Height: | Size: 555 B |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 743 B |
|
Before Width: | Height: | Size: 840 B |
|
Before Width: | Height: | Size: 10 KiB |
@@ -1,50 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 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.
|
||||
-->
|
||||
<RelativeLayout
|
||||
android:id="@+id/meat_container"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="150dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/meat_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@drawable/p1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/meat_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:shadowColor="#000000"
|
||||
android:shadowDx="0"
|
||||
android:shadowDy="0"
|
||||
android:shadowRadius="10"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Hello"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -1,46 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 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.
|
||||
-->
|
||||
<RelativeLayout
|
||||
android:id="@+id/meat_container"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/meat_image"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@drawable/p1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/meat_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:layout_toEndOf="@id/meat_image"
|
||||
android:layout_centerInParent="true"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="24sp"
|
||||
tools:text="Title"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -1,280 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
package com.example.android.adaptertransition;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.transition.AutoTransition;
|
||||
import android.transition.Scene;
|
||||
import android.transition.Transition;
|
||||
import android.transition.TransitionManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* Main screen for AdapterTransition sample.
|
||||
*/
|
||||
public class AdapterTransitionFragment extends Fragment implements Transition.TransitionListener {
|
||||
|
||||
/**
|
||||
* Since the transition framework requires all relevant views in a view hierarchy to be marked
|
||||
* with IDs, we use this ID to mark the root view.
|
||||
*/
|
||||
private static final int ROOT_ID = 1;
|
||||
|
||||
/**
|
||||
* A tag for saving state whether the mAbsListView is ListView or GridView.
|
||||
*/
|
||||
private static final String STATE_IS_LISTVIEW = "is_listview";
|
||||
|
||||
/**
|
||||
* This is where we place our AdapterView (ListView / GridView).
|
||||
*/
|
||||
private FrameLayout mContent;
|
||||
|
||||
/**
|
||||
* This is where we carry out the transition.
|
||||
*/
|
||||
private FrameLayout mCover;
|
||||
|
||||
/**
|
||||
* This list shows our contents. It can be ListView or GridView, and we toggle between them
|
||||
* using the transition framework.
|
||||
*/
|
||||
private AbsListView mAbsListView;
|
||||
|
||||
/**
|
||||
* This is our contents.
|
||||
*/
|
||||
private MeatAdapter mAdapter;
|
||||
|
||||
public static AdapterTransitionFragment newInstance() {
|
||||
return new AdapterTransitionFragment();
|
||||
}
|
||||
|
||||
public AdapterTransitionFragment() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
// If savedInstanceState is available, we restore the state whether the list is a ListView
|
||||
// or a GridView.
|
||||
boolean isListView;
|
||||
if (null == savedInstanceState) {
|
||||
isListView = true;
|
||||
} else {
|
||||
isListView = savedInstanceState.getBoolean(STATE_IS_LISTVIEW, true);
|
||||
}
|
||||
inflateAbsList(inflater, container, isListView);
|
||||
return inflater.inflate(R.layout.fragment_adapter_transition, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(STATE_IS_LISTVIEW, mAbsListView instanceof ListView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
// Retaining references for FrameLayouts that we use later.
|
||||
mContent = (FrameLayout) view.findViewById(R.id.content);
|
||||
mCover = (FrameLayout) view.findViewById(R.id.cover);
|
||||
// We are attaching the list to the screen here.
|
||||
mContent.addView(mAbsListView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.fragment_adapter_transition, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareOptionsMenu(Menu menu) {
|
||||
// We change the look of the icon every time the user toggles between list and grid.
|
||||
MenuItem item = menu.findItem(R.id.action_toggle);
|
||||
if (null != item) {
|
||||
if (mAbsListView instanceof ListView) {
|
||||
item.setIcon(R.drawable.ic_action_grid);
|
||||
item.setTitle(R.string.show_as_grid);
|
||||
} else {
|
||||
item.setIcon(R.drawable.ic_action_list);
|
||||
item.setTitle(R.string.show_as_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_toggle: {
|
||||
toggle();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionStart(Transition transition) {
|
||||
}
|
||||
|
||||
// BEGIN_INCLUDE(on_transition_end)
|
||||
@Override
|
||||
public void onTransitionEnd(Transition transition) {
|
||||
// When the transition ends, we remove all the views from the overlay and hide it.
|
||||
mCover.removeAllViews();
|
||||
mCover.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
// END_INCLUDE(on_transition_end)
|
||||
|
||||
@Override
|
||||
public void onTransitionCancel(Transition transition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionPause(Transition transition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionResume(Transition transition) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflate a ListView or a GridView with a corresponding ListAdapter.
|
||||
*
|
||||
* @param inflater The LayoutInflater.
|
||||
* @param container The ViewGroup that contains this AbsListView. The AbsListView won't be
|
||||
* attached to it.
|
||||
* @param inflateListView Pass true to inflate a ListView, or false to inflate a GridView.
|
||||
*/
|
||||
private void inflateAbsList(LayoutInflater inflater, ViewGroup container,
|
||||
boolean inflateListView) {
|
||||
if (inflateListView) {
|
||||
mAbsListView = (AbsListView) inflater.inflate(R.layout.fragment_meat_list,
|
||||
container, false);
|
||||
mAdapter = new MeatAdapter(inflater, R.layout.item_meat_list);
|
||||
} else {
|
||||
mAbsListView = (AbsListView) inflater.inflate(R.layout.fragment_meat_grid,
|
||||
container, false);
|
||||
mAdapter = new MeatAdapter(inflater, R.layout.item_meat_grid);
|
||||
}
|
||||
mAbsListView.setAdapter(mAdapter);
|
||||
mAbsListView.setOnItemClickListener(mAdapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the UI between ListView and GridView.
|
||||
*/
|
||||
private void toggle() {
|
||||
// We use mCover as the overlay on which we carry out the transition.
|
||||
mCover.setVisibility(View.VISIBLE);
|
||||
// This FrameLayout holds all the visible views in the current list or grid. We use this as
|
||||
// the starting Scene of the Transition later.
|
||||
FrameLayout before = copyVisibleViews();
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
|
||||
mCover.addView(before, params);
|
||||
// Swap the actual list.
|
||||
swapAbsListView();
|
||||
// We also swap the icon for the toggle button.
|
||||
ActivityCompat.invalidateOptionsMenu(getActivity());
|
||||
// It is now ready to start the transition.
|
||||
mAbsListView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// BEGIN_INCLUDE(transition_with_listener)
|
||||
Scene scene = new Scene(mCover, copyVisibleViews());
|
||||
Transition transition = new AutoTransition();
|
||||
transition.addListener(AdapterTransitionFragment.this);
|
||||
TransitionManager.go(scene, transition);
|
||||
// END_INCLUDE(transition_with_listener)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap ListView with GridView, or GridView with ListView.
|
||||
*/
|
||||
private void swapAbsListView() {
|
||||
// We save the current scrolling position before removing the current list.
|
||||
int first = mAbsListView.getFirstVisiblePosition();
|
||||
// If the current list is a GridView, we replace it with a ListView. If it is a ListView,
|
||||
// a GridView.
|
||||
LayoutInflater inflater = LayoutInflater.from(getActivity());
|
||||
inflateAbsList(inflater, (ViewGroup) mAbsListView.getParent(),
|
||||
mAbsListView instanceof GridView);
|
||||
mAbsListView.setAdapter(mAdapter);
|
||||
// We restore the scrolling position here.
|
||||
mAbsListView.setSelection(first);
|
||||
// The new list is ready, and we replace the existing one with it.
|
||||
mContent.removeAllViews();
|
||||
mContent.addView(mAbsListView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all the visible views in the mAbsListView into a new FrameLayout and return it.
|
||||
*
|
||||
* @return a FrameLayout with all the visible views inside.
|
||||
*/
|
||||
private FrameLayout copyVisibleViews() {
|
||||
// This is the FrameLayout we return afterwards.
|
||||
FrameLayout layout = new FrameLayout(getActivity());
|
||||
// The transition framework requires to set ID for all views to be animated.
|
||||
layout.setId(ROOT_ID);
|
||||
// We only copy visible views.
|
||||
int first = mAbsListView.getFirstVisiblePosition();
|
||||
int index = 0;
|
||||
while (true) {
|
||||
// This is one of the views that we copy. Note that the argument for getChildAt is a
|
||||
// zero-oriented index, and it doesn't usually match with its position in the list.
|
||||
View source = mAbsListView.getChildAt(index);
|
||||
if (null == source) {
|
||||
break;
|
||||
}
|
||||
// This is the copy of the original view.
|
||||
View destination = mAdapter.getView(first + index, null, layout);
|
||||
assert destination != null;
|
||||
destination.setId(ROOT_ID + first + index);
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
|
||||
source.getWidth(), source.getHeight());
|
||||
params.leftMargin = (int) source.getX();
|
||||
params.topMargin = (int) source.getY();
|
||||
layout.addView(destination, params);
|
||||
++index;
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
package com.example.android.adaptertransition;
|
||||
|
||||
/**
|
||||
* Sample data.
|
||||
*/
|
||||
public class Meat {
|
||||
|
||||
public int resourceId;
|
||||
public String title;
|
||||
|
||||
public Meat(int resourceId, String title) {
|
||||
this.resourceId = resourceId;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public static final Meat[] MEATS = {
|
||||
new Meat(R.drawable.p1, "First"),
|
||||
new Meat(R.drawable.p2, "Second"),
|
||||
new Meat(R.drawable.p3, "Third"),
|
||||
new Meat(R.drawable.p4, "Fourth"),
|
||||
new Meat(R.drawable.p5, "Fifth"),
|
||||
new Meat(R.drawable.p6, "Sixth"),
|
||||
new Meat(R.drawable.p7, "Seventh"),
|
||||
new Meat(R.drawable.p8, "Eighth"),
|
||||
new Meat(R.drawable.p9, "Ninth"),
|
||||
new Meat(R.drawable.p10, "Tenth"),
|
||||
new Meat(R.drawable.p11, "Eleventh"),
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
package com.example.android.adaptertransition;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* This class provides data as Views. It is designed to support both ListView and GridView by
|
||||
* changing a layout resource file to inflate.
|
||||
*/
|
||||
public class MeatAdapter extends BaseAdapter implements AbsListView.OnItemClickListener {
|
||||
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
private final int mResourceId;
|
||||
|
||||
/**
|
||||
* Create a new instance of {@link MeatAdapter}.
|
||||
*
|
||||
* @param inflater The layout inflater.
|
||||
* @param resourceId The resource ID for the layout to be used. The layout should contain an
|
||||
* ImageView with ID of "meat_image" and a TextView with ID of "meat_title".
|
||||
*/
|
||||
public MeatAdapter(LayoutInflater inflater, int resourceId) {
|
||||
mLayoutInflater = inflater;
|
||||
mResourceId = resourceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return Meat.MEATS.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Meat getItem(int position) {
|
||||
return Meat.MEATS[position];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return Meat.MEATS[position].resourceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final View view;
|
||||
final ViewHolder holder;
|
||||
if (null == convertView) {
|
||||
view = mLayoutInflater.inflate(mResourceId, parent, false);
|
||||
holder = new ViewHolder();
|
||||
assert view != null;
|
||||
holder.image = (ImageView) view.findViewById(R.id.meat_image);
|
||||
holder.title = (TextView) view.findViewById(R.id.meat_title);
|
||||
view.setTag(holder);
|
||||
} else {
|
||||
view = convertView;
|
||||
holder = (ViewHolder) view.getTag();
|
||||
}
|
||||
Meat meat = getItem(position);
|
||||
holder.image.setImageResource(meat.resourceId);
|
||||
holder.title.setText(meat.title);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
ViewHolder holder = (ViewHolder) view.getTag();
|
||||
Context context = view.getContext();
|
||||
if (null != holder && null != holder.title && null != context) {
|
||||
Toast.makeText(context, context.getString(R.string.item_clicked,
|
||||
holder.title.getText()), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ViewHolder {
|
||||
public ImageView image;
|
||||
public TextView title;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.example.android.common.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* To be used with ViewPager to provide a tab indicator component which give constant feedback as to
|
||||
* the user's scroll progress.
|
||||
* <p>
|
||||
* To use the component, simply add it to your view hierarchy. Then in your
|
||||
* {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
|
||||
* {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
|
||||
* <p>
|
||||
* The colors can be customized in two ways. The first and simplest is to provide an array of colors
|
||||
* via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
|
||||
* alternative is via the {@link TabColorizer} interface which provides you complete control over
|
||||
* which color is used for any individual position.
|
||||
* <p>
|
||||
* The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
|
||||
* providing the layout ID of your custom layout.
|
||||
*/
|
||||
public class SlidingTabLayout extends HorizontalScrollView {
|
||||
|
||||
/**
|
||||
* Allows complete control over the colors drawn in the tab layout. Set with
|
||||
* {@link #setCustomTabColorizer(TabColorizer)}.
|
||||
*/
|
||||
public interface TabColorizer {
|
||||
|
||||
/**
|
||||
* @return return the color of the indicator used when {@code position} is selected.
|
||||
*/
|
||||
int getIndicatorColor(int position);
|
||||
|
||||
/**
|
||||
* @return return the color of the divider drawn to the right of {@code position}.
|
||||
*/
|
||||
int getDividerColor(int position);
|
||||
|
||||
}
|
||||
|
||||
private static final int TITLE_OFFSET_DIPS = 24;
|
||||
private static final int TAB_VIEW_PADDING_DIPS = 16;
|
||||
private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
|
||||
|
||||
private int mTitleOffset;
|
||||
|
||||
private int mTabViewLayoutId;
|
||||
private int mTabViewTextViewId;
|
||||
|
||||
private ViewPager mViewPager;
|
||||
private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
|
||||
|
||||
private final SlidingTabStrip mTabStrip;
|
||||
|
||||
public SlidingTabLayout(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public SlidingTabLayout(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
// Disable the Scroll Bar
|
||||
setHorizontalScrollBarEnabled(false);
|
||||
// Make sure that the Tab Strips fills this View
|
||||
setFillViewport(true);
|
||||
|
||||
mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
|
||||
|
||||
mTabStrip = new SlidingTabStrip(context);
|
||||
addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom {@link TabColorizer} to be used.
|
||||
*
|
||||
* If you only require simple custmisation then you can use
|
||||
* {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
|
||||
* similar effects.
|
||||
*/
|
||||
public void setCustomTabColorizer(TabColorizer tabColorizer) {
|
||||
mTabStrip.setCustomTabColorizer(tabColorizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the colors to be used for indicating the selected tab. These colors are treated as a
|
||||
* circular array. Providing one color will mean that all tabs are indicated with the same color.
|
||||
*/
|
||||
public void setSelectedIndicatorColors(int... colors) {
|
||||
mTabStrip.setSelectedIndicatorColors(colors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the colors to be used for tab dividers. These colors are treated as a circular array.
|
||||
* Providing one color will mean that all tabs are indicated with the same color.
|
||||
*/
|
||||
public void setDividerColors(int... colors) {
|
||||
mTabStrip.setDividerColors(colors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
|
||||
* required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
|
||||
* that the layout can update it's scroll position correctly.
|
||||
*
|
||||
* @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
|
||||
*/
|
||||
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
|
||||
mViewPagerPageChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom layout to be inflated for the tab views.
|
||||
*
|
||||
* @param layoutResId Layout id to be inflated
|
||||
* @param textViewId id of the {@link TextView} in the inflated view
|
||||
*/
|
||||
public void setCustomTabView(int layoutResId, int textViewId) {
|
||||
mTabViewLayoutId = layoutResId;
|
||||
mTabViewTextViewId = textViewId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the associated view pager. Note that the assumption here is that the pager content
|
||||
* (number of tabs and tab titles) does not change after this call has been made.
|
||||
*/
|
||||
public void setViewPager(ViewPager viewPager) {
|
||||
mTabStrip.removeAllViews();
|
||||
|
||||
mViewPager = viewPager;
|
||||
if (viewPager != null) {
|
||||
viewPager.setOnPageChangeListener(new InternalViewPagerListener());
|
||||
populateTabStrip();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default view to be used for tabs. This is called if a custom tab view is not set via
|
||||
* {@link #setCustomTabView(int, int)}.
|
||||
*/
|
||||
protected TextView createDefaultTabView(Context context) {
|
||||
TextView textView = new TextView(context);
|
||||
textView.setGravity(Gravity.CENTER);
|
||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
|
||||
textView.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
// If we're running on Honeycomb or newer, then we can use the Theme's
|
||||
// selectableItemBackground to ensure that the View has a pressed state
|
||||
TypedValue outValue = new TypedValue();
|
||||
getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
|
||||
outValue, true);
|
||||
textView.setBackgroundResource(outValue.resourceId);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
// If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
|
||||
textView.setAllCaps(true);
|
||||
}
|
||||
|
||||
int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
|
||||
textView.setPadding(padding, padding, padding, padding);
|
||||
|
||||
return textView;
|
||||
}
|
||||
|
||||
private void populateTabStrip() {
|
||||
final PagerAdapter adapter = mViewPager.getAdapter();
|
||||
final View.OnClickListener tabClickListener = new TabClickListener();
|
||||
|
||||
for (int i = 0; i < adapter.getCount(); i++) {
|
||||
View tabView = null;
|
||||
TextView tabTitleView = null;
|
||||
|
||||
if (mTabViewLayoutId != 0) {
|
||||
// If there is a custom tab view layout id set, try and inflate it
|
||||
tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
|
||||
false);
|
||||
tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
|
||||
}
|
||||
|
||||
if (tabView == null) {
|
||||
tabView = createDefaultTabView(getContext());
|
||||
}
|
||||
|
||||
if (tabTitleView == null && TextView.class.isInstance(tabView)) {
|
||||
tabTitleView = (TextView) tabView;
|
||||
}
|
||||
|
||||
tabTitleView.setText(adapter.getPageTitle(i));
|
||||
tabView.setOnClickListener(tabClickListener);
|
||||
|
||||
mTabStrip.addView(tabView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
if (mViewPager != null) {
|
||||
scrollToTab(mViewPager.getCurrentItem(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void scrollToTab(int tabIndex, int positionOffset) {
|
||||
final int tabStripChildCount = mTabStrip.getChildCount();
|
||||
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
View selectedChild = mTabStrip.getChildAt(tabIndex);
|
||||
if (selectedChild != null) {
|
||||
int targetScrollX = selectedChild.getLeft() + positionOffset;
|
||||
|
||||
if (tabIndex > 0 || positionOffset > 0) {
|
||||
// If we're not at the first child and are mid-scroll, make sure we obey the offset
|
||||
targetScrollX -= mTitleOffset;
|
||||
}
|
||||
|
||||
scrollTo(targetScrollX, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
|
||||
private int mScrollState;
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
int tabStripChildCount = mTabStrip.getChildCount();
|
||||
if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mTabStrip.onViewPagerPageChanged(position, positionOffset);
|
||||
|
||||
View selectedTitle = mTabStrip.getChildAt(position);
|
||||
int extraOffset = (selectedTitle != null)
|
||||
? (int) (positionOffset * selectedTitle.getWidth())
|
||||
: 0;
|
||||
scrollToTab(position, extraOffset);
|
||||
|
||||
if (mViewPagerPageChangeListener != null) {
|
||||
mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
|
||||
positionOffsetPixels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
mScrollState = state;
|
||||
|
||||
if (mViewPagerPageChangeListener != null) {
|
||||
mViewPagerPageChangeListener.onPageScrollStateChanged(state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
|
||||
mTabStrip.onViewPagerPageChanged(position, 0f);
|
||||
scrollToTab(position, 0);
|
||||
}
|
||||
|
||||
if (mViewPagerPageChangeListener != null) {
|
||||
mViewPagerPageChangeListener.onPageSelected(position);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class TabClickListener implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
|
||||
if (v == mTabStrip.getChildAt(i)) {
|
||||
mViewPager.setCurrentItem(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.example.android.common.view;
|
||||
|
||||
import android.R;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
class SlidingTabStrip extends LinearLayout {
|
||||
|
||||
private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
|
||||
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
|
||||
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
|
||||
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
|
||||
|
||||
private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
|
||||
private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
|
||||
private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
|
||||
|
||||
private final int mBottomBorderThickness;
|
||||
private final Paint mBottomBorderPaint;
|
||||
|
||||
private final int mSelectedIndicatorThickness;
|
||||
private final Paint mSelectedIndicatorPaint;
|
||||
|
||||
private final int mDefaultBottomBorderColor;
|
||||
|
||||
private final Paint mDividerPaint;
|
||||
private final float mDividerHeight;
|
||||
|
||||
private int mSelectedPosition;
|
||||
private float mSelectionOffset;
|
||||
|
||||
private SlidingTabLayout.TabColorizer mCustomTabColorizer;
|
||||
private final SimpleTabColorizer mDefaultTabColorizer;
|
||||
|
||||
SlidingTabStrip(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
SlidingTabStrip(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setWillNotDraw(false);
|
||||
|
||||
final float density = getResources().getDisplayMetrics().density;
|
||||
|
||||
TypedValue outValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
|
||||
final int themeForegroundColor = outValue.data;
|
||||
|
||||
mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
|
||||
DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
|
||||
|
||||
mDefaultTabColorizer = new SimpleTabColorizer();
|
||||
mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
|
||||
mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
|
||||
DEFAULT_DIVIDER_COLOR_ALPHA));
|
||||
|
||||
mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
|
||||
mBottomBorderPaint = new Paint();
|
||||
mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
|
||||
|
||||
mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
|
||||
mSelectedIndicatorPaint = new Paint();
|
||||
|
||||
mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
|
||||
mDividerPaint = new Paint();
|
||||
mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
|
||||
}
|
||||
|
||||
void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
|
||||
mCustomTabColorizer = customTabColorizer;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void setSelectedIndicatorColors(int... colors) {
|
||||
// Make sure that the custom colorizer is removed
|
||||
mCustomTabColorizer = null;
|
||||
mDefaultTabColorizer.setIndicatorColors(colors);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void setDividerColors(int... colors) {
|
||||
// Make sure that the custom colorizer is removed
|
||||
mCustomTabColorizer = null;
|
||||
mDefaultTabColorizer.setDividerColors(colors);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void onViewPagerPageChanged(int position, float positionOffset) {
|
||||
mSelectedPosition = position;
|
||||
mSelectionOffset = positionOffset;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
final int height = getHeight();
|
||||
final int childCount = getChildCount();
|
||||
final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
|
||||
final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
|
||||
? mCustomTabColorizer
|
||||
: mDefaultTabColorizer;
|
||||
|
||||
// Thick colored underline below the current selection
|
||||
if (childCount > 0) {
|
||||
View selectedTitle = getChildAt(mSelectedPosition);
|
||||
int left = selectedTitle.getLeft();
|
||||
int right = selectedTitle.getRight();
|
||||
int color = tabColorizer.getIndicatorColor(mSelectedPosition);
|
||||
|
||||
if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
|
||||
int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
|
||||
if (color != nextColor) {
|
||||
color = blendColors(nextColor, color, mSelectionOffset);
|
||||
}
|
||||
|
||||
// Draw the selection partway between the tabs
|
||||
View nextTitle = getChildAt(mSelectedPosition + 1);
|
||||
left = (int) (mSelectionOffset * nextTitle.getLeft() +
|
||||
(1.0f - mSelectionOffset) * left);
|
||||
right = (int) (mSelectionOffset * nextTitle.getRight() +
|
||||
(1.0f - mSelectionOffset) * right);
|
||||
}
|
||||
|
||||
mSelectedIndicatorPaint.setColor(color);
|
||||
|
||||
canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
|
||||
height, mSelectedIndicatorPaint);
|
||||
}
|
||||
|
||||
// Thin underline along the entire bottom edge
|
||||
canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
|
||||
|
||||
// Vertical separators between the titles
|
||||
int separatorTop = (height - dividerHeightPx) / 2;
|
||||
for (int i = 0; i < childCount - 1; i++) {
|
||||
View child = getChildAt(i);
|
||||
mDividerPaint.setColor(tabColorizer.getDividerColor(i));
|
||||
canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
|
||||
separatorTop + dividerHeightPx, mDividerPaint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the alpha value of the {@code color} to be the given {@code alpha} value.
|
||||
*/
|
||||
private static int setColorAlpha(int color, byte alpha) {
|
||||
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
|
||||
}
|
||||
|
||||
/**
|
||||
* Blend {@code color1} and {@code color2} using the given ratio.
|
||||
*
|
||||
* @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
|
||||
* 0.0 will return {@code color2}.
|
||||
*/
|
||||
private static int blendColors(int color1, int color2, float ratio) {
|
||||
final float inverseRation = 1f - ratio;
|
||||
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
|
||||
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
|
||||
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
|
||||
return Color.rgb((int) r, (int) g, (int) b);
|
||||
}
|
||||
|
||||
private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
|
||||
private int[] mIndicatorColors;
|
||||
private int[] mDividerColors;
|
||||
|
||||
@Override
|
||||
public final int getIndicatorColor(int position) {
|
||||
return mIndicatorColors[position % mIndicatorColors.length];
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getDividerColor(int position) {
|
||||
return mDividerColors[position % mDividerColors.length];
|
||||
}
|
||||
|
||||
void setIndicatorColors(int... colors) {
|
||||
mIndicatorColors = colors;
|
||||
}
|
||||
|
||||
void setDividerColors(int... colors) {
|
||||
mDividerColors = colors;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -14,26 +14,21 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/vertical_page_margin"
|
||||
android:paddingLeft="@dimen/horizontal_page_margin"
|
||||
android:paddingRight="@dimen/horizontal_page_margin"
|
||||
android:paddingTop="@dimen/vertical_page_margin">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/status_not_installed" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/unhide"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/unhide" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/say_hello"
|
||||
@@ -41,4 +36,89 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/allow_saying_hello"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@id/message"
|
||||
android:text="@string/message"/>
|
||||
|
||||
<EditText
|
||||
android:id="@id/message"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@id/number"
|
||||
android:text="@string/number"/>
|
||||
|
||||
<EditText
|
||||
android:id="@id/number"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:inputType="number"
|
||||
android:maxLines="1"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/rank"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/rank"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_small"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/approvals"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/approvals"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_small"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.example.android.basicmanagedprofile.MainActivity.MainFragment">
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
|
||||
@@ -14,23 +14,26 @@ 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.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context="com.example.android.adaptertransition.AdapterTransitionFragment">
|
||||
android:paddingBottom="@dimen/vertical_page_margin"
|
||||
android:paddingLeft="@dimen/horizontal_page_margin"
|
||||
android:paddingRight="@dimen/horizontal_page_margin"
|
||||
android:paddingTop="@dimen/vertical_page_margin">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/content"
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/status_not_installed"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/cover"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#f3f3f3"
|
||||
android:visibility="invisible"/>
|
||||
<Button
|
||||
android:id="@+id/unhide"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/unhide"/>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
@@ -14,10 +14,9 @@ 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.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_toggle"
|
||||
android:icon="@drawable/ic_action_grid"
|
||||
android:showAsAction="always|withText"
|
||||
android:title="@string/show_as_grid"/>
|
||||
</menu>
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="@dimen/margin_medium"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:background="#9000"/>
|
||||
@@ -14,8 +14,8 @@ 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.
|
||||
-->
|
||||
<ListView
|
||||
android:id="@+id/abs_list_view"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
<resources>
|
||||
<item name="message" type="id"/>
|
||||
<item name="number" type="id"/>
|
||||
<item name="approval" type="id"/>
|
||||
</resources>
|
||||
@@ -25,4 +25,8 @@
|
||||
<string name="allowed">Allowed</string>
|
||||
<string name="disallowed">Disallowed</string>
|
||||
<string name="profile_name">AppRestrictionEnforcer </string>
|
||||
<string name="message">Message: </string>
|
||||
<string name="number">Number: </string>
|
||||
<string name="rank">Rank: </string>
|
||||
<string name="approvals">Approvals: </string>
|
||||
</resources>
|
||||
|
||||
@@ -22,34 +22,34 @@ import android.content.Context;
|
||||
import android.content.RestrictionEntry;
|
||||
import android.content.RestrictionsManager;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This fragment provides UI and functionality to set restrictions on the AppRestrictionSchema
|
||||
* sample.
|
||||
*/
|
||||
public class AppRestrictionEnforcerFragment extends Fragment implements View.OnClickListener,
|
||||
CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
/**
|
||||
* Package name of the AppRestrictionSchema sample.
|
||||
*/
|
||||
private static final String PACKAGE_NAME_APP_RESTRICTION_SCHEMA
|
||||
= "com.example.android.apprestrictionschema";
|
||||
public class AppRestrictionEnforcerFragment extends Fragment implements
|
||||
CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
|
||||
|
||||
/**
|
||||
* Key for {@link SharedPreferences}
|
||||
@@ -62,15 +62,38 @@ public class AppRestrictionEnforcerFragment extends Fragment implements View.OnC
|
||||
private static final String RESTRICTION_KEY_SAY_HELLO = "can_say_hello";
|
||||
|
||||
/**
|
||||
* Default boolean value for "can_say_hello" restriction. The actual value is loaded in
|
||||
* {@link #loadRestrictions(android.app.Activity)}.
|
||||
* Key for the string restriction in AppRestrictionSchema.
|
||||
*/
|
||||
private boolean mDefaultValueRestrictionSayHello;
|
||||
private static final String RESTRICTION_KEY_MESSAGE = "message";
|
||||
|
||||
/**
|
||||
* Key for the integer restriction in AppRestrictionSchema.
|
||||
*/
|
||||
private static final String RESTRICTION_KEY_NUMBER = "number";
|
||||
|
||||
/**
|
||||
* Key for the choice restriction in AppRestrictionSchema.
|
||||
*/
|
||||
private static final String RESTRICTION_KEY_RANK = "rank";
|
||||
|
||||
/**
|
||||
* Key for the multi-select restriction in AppRestrictionSchema.
|
||||
*/
|
||||
private static final String RESTRICTION_KEY_APPROVALS = "approvals";
|
||||
|
||||
private static final String DELIMETER = ",";
|
||||
|
||||
/**
|
||||
* Current status of the restrictions.
|
||||
*/
|
||||
private Bundle mCurrentRestrictions = new Bundle();
|
||||
|
||||
// UI Components
|
||||
private TextView mTextStatus;
|
||||
private Button mButtonUnhide;
|
||||
private Switch mSwitchSayHello;
|
||||
private EditText mEditMessage;
|
||||
private EditText mEditNumber;
|
||||
private Spinner mSpinnerRank;
|
||||
private LinearLayout mLayoutApprovals;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@@ -80,99 +103,72 @@ public class AppRestrictionEnforcerFragment extends Fragment implements View.OnC
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
mTextStatus = (TextView) view.findViewById(R.id.status);
|
||||
mButtonUnhide = (Button) view.findViewById(R.id.unhide);
|
||||
// Retain references for the UI elements
|
||||
mSwitchSayHello = (Switch) view.findViewById(R.id.say_hello);
|
||||
mButtonUnhide.setOnClickListener(this);
|
||||
mSwitchSayHello.setOnCheckedChangeListener(this);
|
||||
mEditMessage = (EditText) view.findViewById(R.id.message);
|
||||
mEditNumber = (EditText) view.findViewById(R.id.number);
|
||||
mSpinnerRank = (Spinner) view.findViewById(R.id.rank);
|
||||
mLayoutApprovals = (LinearLayout) view.findViewById(R.id.approvals);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateUi(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.unhide: {
|
||||
unhideApp(getActivity());
|
||||
break;
|
||||
}
|
||||
}
|
||||
loadRestrictions(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
|
||||
switch (compoundButton.getId()) {
|
||||
case R.id.say_hello: {
|
||||
allowSayHello(getActivity(), checked);
|
||||
saveCanSayHello(getActivity(), checked);
|
||||
break;
|
||||
}
|
||||
case R.id.approval: {
|
||||
if (checked) {
|
||||
addApproval(getActivity(), (String) compoundButton.getTag());
|
||||
} else {
|
||||
removeApproval(getActivity(), (String) compoundButton.getTag());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI components according to the current status of AppRestrictionSchema and its
|
||||
* restriction.
|
||||
*
|
||||
* @param activity The activity
|
||||
*/
|
||||
private void updateUi(Activity activity) {
|
||||
PackageManager packageManager = activity.getPackageManager();
|
||||
private TextWatcher mWatcherMessage = new EasyTextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
saveMessage(getActivity(), s.toString());
|
||||
}
|
||||
};
|
||||
|
||||
private TextWatcher mWatcherNumber = new EasyTextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
try {
|
||||
ApplicationInfo info = packageManager.getApplicationInfo(
|
||||
PACKAGE_NAME_APP_RESTRICTION_SCHEMA, PackageManager.GET_UNINSTALLED_PACKAGES);
|
||||
DevicePolicyManager devicePolicyManager =
|
||||
(DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
|
||||
if (0 < (info.flags & ApplicationInfo.FLAG_INSTALLED)) {
|
||||
if (!devicePolicyManager.isApplicationHidden(
|
||||
EnforcerDeviceAdminReceiver.getComponentName(activity),
|
||||
PACKAGE_NAME_APP_RESTRICTION_SCHEMA)) {
|
||||
// The app is ready
|
||||
loadRestrictions(activity);
|
||||
mTextStatus.setVisibility(View.GONE);
|
||||
mButtonUnhide.setVisibility(View.GONE);
|
||||
mSwitchSayHello.setVisibility(View.VISIBLE);
|
||||
mSwitchSayHello.setOnCheckedChangeListener(null);
|
||||
mSwitchSayHello.setChecked(canSayHello(activity));
|
||||
mSwitchSayHello.setOnCheckedChangeListener(this);
|
||||
} else {
|
||||
// The app is installed but hidden in this profile
|
||||
mTextStatus.setText(R.string.status_not_activated);
|
||||
mTextStatus.setVisibility(View.VISIBLE);
|
||||
mButtonUnhide.setVisibility(View.VISIBLE);
|
||||
mSwitchSayHello.setVisibility(View.GONE);
|
||||
String string = s.toString();
|
||||
if (!TextUtils.isEmpty(string)) {
|
||||
saveNumber(getActivity(), Integer.parseInt(string));
|
||||
}
|
||||
} else {
|
||||
// Need to reinstall the sample app
|
||||
mTextStatus.setText(R.string.status_need_reinstall);
|
||||
mTextStatus.setVisibility(View.VISIBLE);
|
||||
mButtonUnhide.setVisibility(View.GONE);
|
||||
mSwitchSayHello.setVisibility(View.GONE);
|
||||
} catch (NumberFormatException e) {
|
||||
Toast.makeText(getActivity(), "Not an integer!", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
switch (parent.getId()) {
|
||||
case R.id.rank: {
|
||||
saveRank(getActivity(), (String) parent.getAdapter().getItem(position));
|
||||
break;
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
mTextStatus.setText(R.string.status_not_installed);
|
||||
mTextStatus.setVisibility(View.VISIBLE);
|
||||
mButtonUnhide.setVisibility(View.GONE);
|
||||
mSwitchSayHello.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhides the AppRestrictionSchema sample in case it is hidden in this profile.
|
||||
*
|
||||
* @param activity The activity
|
||||
*/
|
||||
private void unhideApp(Activity activity) {
|
||||
DevicePolicyManager devicePolicyManager =
|
||||
(DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
|
||||
devicePolicyManager.setApplicationHidden(
|
||||
EnforcerDeviceAdminReceiver.getComponentName(activity),
|
||||
PACKAGE_NAME_APP_RESTRICTION_SCHEMA, false);
|
||||
Toast.makeText(activity, "Enabled the app", Toast.LENGTH_SHORT).show();
|
||||
updateUi(activity);
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,50 +178,187 @@ public class AppRestrictionEnforcerFragment extends Fragment implements View.OnC
|
||||
* @param activity The activity
|
||||
*/
|
||||
private void loadRestrictions(Activity activity) {
|
||||
RestrictionsManager restrictionsManager =
|
||||
RestrictionsManager manager =
|
||||
(RestrictionsManager) activity.getSystemService(Context.RESTRICTIONS_SERVICE);
|
||||
List<RestrictionEntry> restrictions =
|
||||
restrictionsManager.getManifestRestrictions(PACKAGE_NAME_APP_RESTRICTION_SCHEMA);
|
||||
for (RestrictionEntry restriction : restrictions) {
|
||||
if (RESTRICTION_KEY_SAY_HELLO.equals(restriction.getKey())) {
|
||||
mDefaultValueRestrictionSayHello = restriction.getSelectedState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the AppRestrictionSchema is currently allowed to say hello to its user. Note
|
||||
* that a profile/device owner needs to remember each restriction value on its own.
|
||||
*
|
||||
* @param activity The activity
|
||||
* @return True if the AppRestrictionSchema is allowed to say hello
|
||||
*/
|
||||
private boolean canSayHello(Activity activity) {
|
||||
manager.getManifestRestrictions(Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA);
|
||||
SharedPreferences prefs = activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);
|
||||
return prefs.getBoolean(RESTRICTION_KEY_SAY_HELLO, mDefaultValueRestrictionSayHello);
|
||||
for (RestrictionEntry restriction : restrictions) {
|
||||
String key = restriction.getKey();
|
||||
if (RESTRICTION_KEY_SAY_HELLO.equals(key)) {
|
||||
updateCanSayHello(prefs.getBoolean(RESTRICTION_KEY_SAY_HELLO,
|
||||
restriction.getSelectedState()));
|
||||
} else if (RESTRICTION_KEY_MESSAGE.equals(key)) {
|
||||
updateMessage(prefs.getString(RESTRICTION_KEY_MESSAGE,
|
||||
restriction.getSelectedString()));
|
||||
} else if (RESTRICTION_KEY_NUMBER.equals(key)) {
|
||||
updateNumber(prefs.getInt(RESTRICTION_KEY_NUMBER,
|
||||
restriction.getIntValue()));
|
||||
} else if (RESTRICTION_KEY_RANK.equals(key)) {
|
||||
updateRank(activity, restriction.getChoiceValues(),
|
||||
prefs.getString(RESTRICTION_KEY_RANK, restriction.getSelectedString()));
|
||||
} else if (RESTRICTION_KEY_APPROVALS.equals(key)) {
|
||||
updateApprovals(activity, restriction.getChoiceValues(),
|
||||
TextUtils.split(prefs.getString(RESTRICTION_KEY_APPROVALS,
|
||||
TextUtils.join(DELIMETER,
|
||||
restriction.getAllSelectedStrings())),
|
||||
DELIMETER));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCanSayHello(boolean canSayHello) {
|
||||
mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, canSayHello);
|
||||
mSwitchSayHello.setOnCheckedChangeListener(null);
|
||||
mSwitchSayHello.setChecked(canSayHello);
|
||||
mSwitchSayHello.setOnCheckedChangeListener(this);
|
||||
}
|
||||
|
||||
private void updateMessage(String message) {
|
||||
mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message);
|
||||
mEditMessage.removeTextChangedListener(mWatcherMessage);
|
||||
mEditMessage.setText(message);
|
||||
mEditMessage.addTextChangedListener(mWatcherMessage);
|
||||
}
|
||||
|
||||
private void updateNumber(int number) {
|
||||
mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number);
|
||||
mEditNumber.removeTextChangedListener(mWatcherNumber);
|
||||
mEditNumber.setText(String.valueOf(number));
|
||||
mEditNumber.addTextChangedListener(mWatcherNumber);
|
||||
}
|
||||
|
||||
private void updateRank(Context context, String[] ranks, String selectedRank) {
|
||||
mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, selectedRank);
|
||||
mSpinnerRank.setAdapter(new ArrayAdapter<>(context,
|
||||
android.R.layout.simple_spinner_dropdown_item, ranks));
|
||||
mSpinnerRank.setSelection(search(ranks, selectedRank));
|
||||
mSpinnerRank.setOnItemSelectedListener(this);
|
||||
}
|
||||
|
||||
private void updateApprovals(Context context, String[] approvals,
|
||||
String[] selectedApprovals) {
|
||||
mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, selectedApprovals);
|
||||
mLayoutApprovals.removeAllViews();
|
||||
for (String approval : approvals) {
|
||||
Switch sw = new Switch(context);
|
||||
sw.setText(approval);
|
||||
sw.setTag(approval);
|
||||
sw.setChecked(Arrays.asList(selectedApprovals).contains(approval));
|
||||
sw.setOnCheckedChangeListener(this);
|
||||
sw.setId(R.id.approval);
|
||||
mLayoutApprovals.addView(sw);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for the "cay_say_hello" restriction of AppRestrictionSchema.
|
||||
* Saves the value for the "cay_say_hello" restriction of AppRestrictionSchema.
|
||||
*
|
||||
* @param activity The activity
|
||||
* @param allow The value to be set for the restriction.
|
||||
*/
|
||||
private void allowSayHello(Activity activity, boolean allow) {
|
||||
private void saveCanSayHello(Activity activity, boolean allow) {
|
||||
mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow);
|
||||
saveRestrictions(activity);
|
||||
// Note that the owner app needs to remember the restrictions on its own.
|
||||
editPreferences(activity).putBoolean(RESTRICTION_KEY_SAY_HELLO, allow).apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the value for the "message" restriction of AppRestrictionSchema.
|
||||
*
|
||||
* @param activity The activity
|
||||
* @param message The value to be set for the restriction.
|
||||
*/
|
||||
private void saveMessage(Activity activity, String message) {
|
||||
mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message);
|
||||
saveRestrictions(activity);
|
||||
editPreferences(activity).putString(RESTRICTION_KEY_MESSAGE, message).apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the value for the "number" restriction of AppRestrictionSchema.
|
||||
*
|
||||
* @param activity The activity
|
||||
* @param number The value to be set for the restriction.
|
||||
*/
|
||||
private void saveNumber(Activity activity, int number) {
|
||||
mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number);
|
||||
saveRestrictions(activity);
|
||||
editPreferences(activity).putInt(RESTRICTION_KEY_NUMBER, number).apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the value for the "rank" restriction of AppRestrictionSchema.
|
||||
*
|
||||
* @param activity The activity
|
||||
* @param rank The value to be set for the restriction.
|
||||
*/
|
||||
private void saveRank(Activity activity, String rank) {
|
||||
mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, rank);
|
||||
saveRestrictions(activity);
|
||||
editPreferences(activity).putString(RESTRICTION_KEY_RANK, rank).apply();
|
||||
}
|
||||
|
||||
private void addApproval(Activity activity, String approval) {
|
||||
List<String> approvals = new ArrayList<>(Arrays.asList(
|
||||
mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS)));
|
||||
if (approvals.contains(approval)) {
|
||||
return;
|
||||
}
|
||||
approvals.add(approval);
|
||||
saveApprovals(activity, approvals.toArray(new String[approvals.size()]));
|
||||
}
|
||||
|
||||
private void removeApproval(Activity activity, String approval) {
|
||||
List<String> approvals = new ArrayList<>(Arrays.asList(
|
||||
mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS)));
|
||||
if (!approval.contains(approval)) {
|
||||
return;
|
||||
}
|
||||
approvals.remove(approval);
|
||||
saveApprovals(activity, approvals.toArray(new String[approvals.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the value for the "approvals" restriction of AppRestrictionSchema.
|
||||
*
|
||||
* @param activity The activity
|
||||
* @param approvals The value to be set for the restriction.
|
||||
*/
|
||||
private void saveApprovals(Activity activity, String[] approvals) {
|
||||
mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, approvals);
|
||||
saveRestrictions(activity);
|
||||
editPreferences(activity).putString(RESTRICTION_KEY_APPROVALS,
|
||||
TextUtils.join(DELIMETER, approvals)).apply();
|
||||
}
|
||||
|
||||
private void saveRestrictions(Activity activity) {
|
||||
DevicePolicyManager devicePolicyManager
|
||||
= (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||
Bundle restrictions = new Bundle();
|
||||
restrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow);
|
||||
devicePolicyManager.setApplicationRestrictions(
|
||||
EnforcerDeviceAdminReceiver.getComponentName(activity),
|
||||
PACKAGE_NAME_APP_RESTRICTION_SCHEMA, restrictions);
|
||||
// The profile/device owner needs to remember the current state of restrictions on its own
|
||||
activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow)
|
||||
.apply();
|
||||
Toast.makeText(activity, allow ? R.string.allowed : R.string.disallowed,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, mCurrentRestrictions);
|
||||
}
|
||||
|
||||
private SharedPreferences.Editor editPreferences(Activity activity) {
|
||||
return activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE).edit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sequential search
|
||||
*
|
||||
* @param array The string array
|
||||
* @param s The string to search for
|
||||
* @return Index if found. -1 if not found.
|
||||
*/
|
||||
private int search(String[] array, String s) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (s.equals(array[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,16 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.example.android.wearable.embeddedapp;
|
||||
package com.example.android.apprestrictionenforcer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
public interface Constants {
|
||||
|
||||
public class PhoneActivity extends Activity {
|
||||
/**
|
||||
* Package name of the AppRestrictionSchema sample.
|
||||
*/
|
||||
public static final String PACKAGE_NAME_APP_RESTRICTION_SCHEMA
|
||||
= "com.example.android.apprestrictionschema";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_phone);
|
||||
}
|
||||
}
|
||||
@@ -14,16 +14,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.example.android.wearable.embeddedapp;
|
||||
package com.example.android.apprestrictionenforcer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextWatcher;
|
||||
|
||||
public class WearableActivity extends Activity {
|
||||
/**
|
||||
* This is a wrapper around {@link TextWatcher} that overrides
|
||||
* {@link TextWatcher#beforeTextChanged(CharSequence, int, int, int)} and
|
||||
* {@link TextWatcher#onTextChanged(CharSequence, int, int, int)} with empty bodies.
|
||||
*/
|
||||
public abstract class EasyTextWatcher implements TextWatcher {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_wearable);
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,24 +18,44 @@ package com.example.android.apprestrictionenforcer;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
public class MainActivity extends FragmentActivity {
|
||||
public class MainActivity extends FragmentActivity implements StatusFragment.StatusUpdatedListener {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main_real);
|
||||
if (null == savedInstanceState) {
|
||||
DevicePolicyManager manager = (DevicePolicyManager)
|
||||
getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||
if (manager.isProfileOwnerApp(getApplicationContext().getPackageName())) {
|
||||
// If the managed profile is already set up, we show the main screen.
|
||||
showMainFragment();
|
||||
} else {
|
||||
// If not, we show the set up screen.
|
||||
DevicePolicyManager devicePolicyManager =
|
||||
(DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||
PackageManager packageManager = getPackageManager();
|
||||
if (!devicePolicyManager.isProfileOwnerApp(getApplicationContext().getPackageName())) {
|
||||
// If the managed profile is not yet set up, we show the setup screen.
|
||||
showSetupProfile();
|
||||
} else {
|
||||
try {
|
||||
ApplicationInfo info = packageManager.getApplicationInfo(
|
||||
Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA,
|
||||
PackageManager.GET_UNINSTALLED_PACKAGES);
|
||||
if (0 == (info.flags & ApplicationInfo.FLAG_INSTALLED)) {
|
||||
// Need to reinstall the sample app
|
||||
showStatusProfile();
|
||||
} else if (devicePolicyManager.isApplicationHidden(
|
||||
EnforcerDeviceAdminReceiver.getComponentName(this),
|
||||
Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA)) {
|
||||
// The app is installed but hidden in this profile
|
||||
showStatusProfile();
|
||||
} else {
|
||||
// Everything is clear; show the main screen
|
||||
showMainFragment();
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
showStatusProfile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,10 +66,21 @@ public class MainActivity extends FragmentActivity {
|
||||
.commit();
|
||||
}
|
||||
|
||||
private void showStatusProfile() {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.container, new StatusFragment())
|
||||
.commit();
|
||||
}
|
||||
|
||||
private void showMainFragment() {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.container, new AppRestrictionEnforcerFragment())
|
||||
.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusUpdated() {
|
||||
showMainFragment();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.android.apprestrictionenforcer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* Provides UI for enabling the target app in this profile. The status of the app can be
|
||||
* uninstalled, hidden, or enabled depending on the situations. This fragment shows suitable
|
||||
* controls for each status.
|
||||
*/
|
||||
public class StatusFragment extends Fragment implements View.OnClickListener {
|
||||
|
||||
private TextView mTextStatus;
|
||||
private Button mButtonUnhide;
|
||||
private StatusUpdatedListener mListener;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_status, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
mTextStatus = (TextView) view.findViewById(R.id.status);
|
||||
mButtonUnhide = (Button) view.findViewById(R.id.unhide);
|
||||
mButtonUnhide.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
mListener = (StatusUpdatedListener) activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
mListener = null;
|
||||
super.onDetach();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateUi(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.unhide: {
|
||||
unhideApp(getActivity());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUi(Activity activity) {
|
||||
PackageManager packageManager = activity.getPackageManager();
|
||||
try {
|
||||
ApplicationInfo info = packageManager.getApplicationInfo(
|
||||
Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA,
|
||||
PackageManager.GET_UNINSTALLED_PACKAGES);
|
||||
DevicePolicyManager devicePolicyManager =
|
||||
(DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
|
||||
if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
|
||||
if (!devicePolicyManager.isApplicationHidden(
|
||||
EnforcerDeviceAdminReceiver.getComponentName(activity),
|
||||
Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA)) {
|
||||
// The app is ready to enforce restrictions
|
||||
// This is unlikely to happen in this sample as unhideApp() handles it.
|
||||
mListener.onStatusUpdated();
|
||||
} else {
|
||||
// The app is installed but hidden in this profile
|
||||
mTextStatus.setText(R.string.status_not_activated);
|
||||
mButtonUnhide.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
// Need to reinstall the sample app
|
||||
mTextStatus.setText(R.string.status_need_reinstall);
|
||||
mButtonUnhide.setVisibility(View.GONE);
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// Need to reinstall the sample app
|
||||
mTextStatus.setText(R.string.status_need_reinstall);
|
||||
mButtonUnhide.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhides the AppRestrictionSchema sample in case it is hidden in this profile.
|
||||
*
|
||||
* @param activity The activity
|
||||
*/
|
||||
private void unhideApp(Activity activity) {
|
||||
DevicePolicyManager devicePolicyManager =
|
||||
(DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
|
||||
devicePolicyManager.setApplicationHidden(
|
||||
EnforcerDeviceAdminReceiver.getComponentName(activity),
|
||||
Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, false);
|
||||
Toast.makeText(activity, "Enabled the app", Toast.LENGTH_SHORT).show();
|
||||
mListener.onStatusUpdated();
|
||||
}
|
||||
|
||||
public interface StatusUpdatedListener {
|
||||
public void onStatusUpdated();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,9 +14,16 @@ 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"
|
||||
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/margin_medium">
|
||||
|
||||
@@ -24,8 +31,8 @@ limitations under the License.
|
||||
android:id="@+id/say_hello_explanation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/explanation_can_say_hello_true"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
tools:text="@string/explanation_can_say_hello_true"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/say_hello"
|
||||
@@ -34,4 +41,33 @@ limitations under the License.
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:text="@string/action_can_say_hello"/>
|
||||
|
||||
<include layout="@layout/separator"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/your_number"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
tools:text="@string/your_number"/>
|
||||
|
||||
<include layout="@layout/separator"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/your_rank"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
tools:text="@string/your_rank"/>
|
||||
|
||||
<include layout="@layout/separator"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/approvals_you_have"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
tools:text="@string/approvals_you_have"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
@@ -14,16 +14,9 @@ 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.
|
||||
-->
|
||||
<GridView
|
||||
android:id="@+id/abs_list_view"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:columnWidth="150dp"
|
||||
android:horizontalSpacing="1dp"
|
||||
android:numColumns="auto_fit"
|
||||
android:padding="1dp"
|
||||
android:scrollbars="none"
|
||||
android:stretchMode="columnWidth"
|
||||
android:verticalSpacing="1dp"/>
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="@dimen/margin_medium"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:background="#9000"/>
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 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.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Bool restriction -->
|
||||
<string name="title_can_say_hello">Can say hello</string>
|
||||
<string name="description_can_say_hello">Whether the app can say hello to the user</string>
|
||||
<bool name="default_can_say_hello">false</bool>
|
||||
|
||||
<!-- String restriction -->
|
||||
<string name="title_message">Message</string>
|
||||
<string name="description_message">A message string to show</string>
|
||||
<string name="default_message">Hello!</string>
|
||||
|
||||
<!-- Integer restriction -->
|
||||
<string name="title_number">Number</string>
|
||||
<string name="description_number">Sample integer value</string>
|
||||
<integer name="default_number">32582657</integer>
|
||||
|
||||
<!-- Choice restriction -->
|
||||
<string name="title_rank">Rank</string>
|
||||
<string name="description_rank">Rank of the user</string>
|
||||
<string-array name="entries_rank">
|
||||
<item>Apprentice</item>
|
||||
<item>Intermediate</item>
|
||||
<item>Master</item>
|
||||
</string-array>
|
||||
<string name="entry_value_rank_apprentice">apprentice</string>
|
||||
<string name="entry_value_rank_intermediate">intermediate</string>
|
||||
<string name="entry_value_rank_master">master</string>
|
||||
<string-array name="entry_values_rank">
|
||||
<item>@string/entry_value_rank_apprentice</item>
|
||||
<item>@string/entry_value_rank_intermediate</item>
|
||||
<item>@string/entry_value_rank_master</item>
|
||||
</string-array>
|
||||
<string name="default_rank">@string/entry_value_rank_apprentice</string>
|
||||
|
||||
<!-- Multi-select restriction -->
|
||||
<string name="title_approvals">Approvals</string>
|
||||
<string name="description_approvals">Approvals</string>
|
||||
<string-array name="entries_approvals">
|
||||
<item>Read</item>
|
||||
<item>Write</item>
|
||||
<item>Execute</item>
|
||||
</string-array>
|
||||
<string name="entry_value_approvals_read">read</string>
|
||||
<string name="entry_value_approvals_write">write</string>
|
||||
<string name="entry_value_approvals_execute">execute</string>
|
||||
<string-array name="entry_values_approvals">
|
||||
<item>@string/entry_value_approvals_read</item>
|
||||
<item>@string/entry_value_approvals_write</item>
|
||||
<item>@string/entry_value_approvals_execute</item>
|
||||
</string-array>
|
||||
<string-array name="default_approvals">
|
||||
<!-- Empty -->
|
||||
</string-array>
|
||||
|
||||
<!-- Hidden restriction -->
|
||||
<string name="title_secret_code">Secret code</string>
|
||||
<string name="description_secret_code">This restriction is hidden and will not be shown to the administrator.</string>
|
||||
<string name="default_secret_code">(Hidden restriction must have some default value)</string>
|
||||
|
||||
</resources>
|
||||
@@ -16,11 +16,14 @@ limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<string name="title_can_say_hello">Can say hello</string>
|
||||
<string name="description_can_say_hello">Whether the app can say hello to the user</string>
|
||||
<string name="explanation_can_say_hello_true">I can say hello to you.</string>
|
||||
<string name="explanation_can_say_hello_false">I am restricted from saying hello to you.</string>
|
||||
<string name="action_can_say_hello">Say hello</string>
|
||||
<string name="message_hello">Hello!</string>
|
||||
<string name="message">All I can say is \"%s\".</string>
|
||||
|
||||
<string name="your_number">Your number: %d</string>
|
||||
<string name="your_rank">Your rank: %s</string>
|
||||
<string name="approvals_you_have">Approvals you have: %s</string>
|
||||
<string name="none">none</string>
|
||||
|
||||
</resources>
|
||||
@@ -16,11 +16,55 @@ limitations under the License.
|
||||
-->
|
||||
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!--
|
||||
Refer to the javadoc of RestrictionsManager for detail of this file.
|
||||
https://developer.android.com/reference/android/content/RestrictionsManager.html
|
||||
-->
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:defaultValue="@bool/default_can_say_hello"
|
||||
android:description="@string/description_can_say_hello"
|
||||
android:key="can_say_hello"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/title_can_say_hello"/>
|
||||
|
||||
<restriction
|
||||
android:defaultValue="@string/default_message"
|
||||
android:description="@string/description_message"
|
||||
android:key="message"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/title_message"/>
|
||||
|
||||
<restriction
|
||||
android:defaultValue="@integer/default_number"
|
||||
android:description="@string/description_number"
|
||||
android:key="number"
|
||||
android:restrictionType="integer"
|
||||
android:title="@string/title_number"/>
|
||||
|
||||
<restriction
|
||||
android:defaultValue="@string/default_rank"
|
||||
android:description="@string/description_rank"
|
||||
android:entries="@array/entries_rank"
|
||||
android:entryValues="@array/entry_values_rank"
|
||||
android:key="rank"
|
||||
android:restrictionType="choice"
|
||||
android:title="@string/title_rank"/>
|
||||
|
||||
<restriction
|
||||
android:defaultValue="@array/default_approvals"
|
||||
android:description="@string/description_approvals"
|
||||
android:entries="@array/entries_approvals"
|
||||
android:entryValues="@array/entry_values_approvals"
|
||||
android:key="approvals"
|
||||
android:restrictionType="multi-select"
|
||||
android:title="@string/title_approvals"/>
|
||||
|
||||
<restriction
|
||||
android:defaultValue="@string/default_secret_code"
|
||||
android:description="@string/description_secret_code"
|
||||
android:key="secret_code"
|
||||
android:restrictionType="hidden"
|
||||
android:title="@string/title_secret_code"/>
|
||||
|
||||
</restrictions>
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
package com.example.android.apprestrictionschema;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.RestrictionEntry;
|
||||
import android.content.RestrictionsManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -30,6 +32,8 @@ import android.widget.Toast;
|
||||
|
||||
import com.example.android.common.logger.Log;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Pressing the button on this fragment pops up a simple Toast message. The button is enabled or
|
||||
* disabled according to the restrictions set by device/profile owner. You can use the
|
||||
@@ -40,9 +44,21 @@ public class AppRestrictionSchemaFragment extends Fragment implements View.OnCli
|
||||
// Tag for the logger
|
||||
private static final String TAG = "AppRestrictionSchemaFragment";
|
||||
|
||||
private static final String KEY_CAN_SAY_HELLO = "can_say_hello";
|
||||
private static final String KEY_MESSAGE = "message";
|
||||
private static final String KEY_NUMBER = "number";
|
||||
private static final String KEY_RANK = "rank";
|
||||
private static final String KEY_APPROVALS = "approvals";
|
||||
|
||||
// Message to show when the button is clicked (String restriction)
|
||||
private String mMessage;
|
||||
|
||||
// UI Components
|
||||
private TextView mTextSayHello;
|
||||
private Button mButtonSayHello;
|
||||
private TextView mTextNumber;
|
||||
private TextView mTextRank;
|
||||
private TextView mTextApprovals;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@@ -54,48 +70,103 @@ public class AppRestrictionSchemaFragment extends Fragment implements View.OnCli
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
mTextSayHello = (TextView) view.findViewById(R.id.say_hello_explanation);
|
||||
mButtonSayHello = (Button) view.findViewById(R.id.say_hello);
|
||||
mTextNumber = (TextView) view.findViewById(R.id.your_number);
|
||||
mTextRank = (TextView) view.findViewById(R.id.your_rank);
|
||||
mTextApprovals = (TextView) view.findViewById(R.id.approvals_you_have);
|
||||
mButtonSayHello.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
// Update the UI according to the configured restrictions
|
||||
RestrictionsManager restrictionsManager =
|
||||
resolveRestrictions();
|
||||
}
|
||||
|
||||
private void resolveRestrictions() {
|
||||
RestrictionsManager manager =
|
||||
(RestrictionsManager) getActivity().getSystemService(Context.RESTRICTIONS_SERVICE);
|
||||
Bundle restrictions = restrictionsManager.getApplicationRestrictions();
|
||||
updateUI(restrictions);
|
||||
Bundle restrictions = manager.getApplicationRestrictions();
|
||||
List<RestrictionEntry> entries = manager.getManifestRestrictions(getActivity().getApplicationContext().getPackageName());
|
||||
for (RestrictionEntry entry : entries) {
|
||||
String key = entry.getKey();
|
||||
Log.d(TAG, "key: " + key);
|
||||
if (key.equals(KEY_CAN_SAY_HELLO)) {
|
||||
updateCanSayHello(entry, restrictions);
|
||||
} else if (key.equals(KEY_MESSAGE)) {
|
||||
updateMessage(entry, restrictions);
|
||||
} else if (key.equals(KEY_NUMBER)) {
|
||||
updateNumber(entry, restrictions);
|
||||
} else if (key.equals(KEY_RANK)) {
|
||||
updateRank(entry, restrictions);
|
||||
} else if (key.equals(KEY_APPROVALS)) {
|
||||
updateApprovals(entry, restrictions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUI(Bundle restrictions) {
|
||||
if (canSayHello(restrictions)) {
|
||||
mTextSayHello.setText(R.string.explanation_can_say_hello_true);
|
||||
mButtonSayHello.setEnabled(true);
|
||||
private void updateCanSayHello(RestrictionEntry entry, Bundle restrictions) {
|
||||
boolean canSayHello;
|
||||
if (restrictions == null || !restrictions.containsKey(KEY_CAN_SAY_HELLO)) {
|
||||
canSayHello = entry.getSelectedState();
|
||||
} else {
|
||||
mTextSayHello.setText(R.string.explanation_can_say_hello_false);
|
||||
mButtonSayHello.setEnabled(false);
|
||||
canSayHello = restrictions.getBoolean(KEY_CAN_SAY_HELLO);
|
||||
}
|
||||
mTextSayHello.setText(canSayHello ?
|
||||
R.string.explanation_can_say_hello_true :
|
||||
R.string.explanation_can_say_hello_false);
|
||||
mButtonSayHello.setEnabled(canSayHello);
|
||||
}
|
||||
|
||||
private void updateMessage(RestrictionEntry entry, Bundle restrictions) {
|
||||
if (restrictions == null || !restrictions.containsKey(KEY_MESSAGE)) {
|
||||
mMessage = entry.getSelectedString();
|
||||
} else {
|
||||
mMessage = restrictions.getString(KEY_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current status of the restriction.
|
||||
*
|
||||
* @param restrictions The application restrictions
|
||||
* @return True if the app is allowed to say hello
|
||||
*/
|
||||
private boolean canSayHello(Bundle restrictions) {
|
||||
final boolean defaultValue = false;
|
||||
boolean canSayHello = restrictions == null ? defaultValue :
|
||||
restrictions.getBoolean("can_say_hello", defaultValue);
|
||||
Log.d(TAG, "canSayHello: " + canSayHello);
|
||||
return canSayHello;
|
||||
private void updateNumber(RestrictionEntry entry, Bundle restrictions) {
|
||||
int number;
|
||||
if (restrictions == null || !restrictions.containsKey(KEY_NUMBER)) {
|
||||
number = entry.getIntValue();
|
||||
} else {
|
||||
number = restrictions.getInt(KEY_NUMBER);
|
||||
}
|
||||
mTextNumber.setText(getString(R.string.your_number, number));
|
||||
}
|
||||
|
||||
private void updateRank(RestrictionEntry entry, Bundle restrictions) {
|
||||
String rank;
|
||||
if (restrictions == null || !restrictions.containsKey(KEY_RANK)) {
|
||||
rank = entry.getSelectedString();
|
||||
} else {
|
||||
rank = restrictions.getString(KEY_RANK);
|
||||
}
|
||||
mTextRank.setText(getString(R.string.your_rank, rank));
|
||||
}
|
||||
|
||||
private void updateApprovals(RestrictionEntry entry, Bundle restrictions) {
|
||||
String[] approvals;
|
||||
if (restrictions == null || !restrictions.containsKey(KEY_APPROVALS)) {
|
||||
approvals = entry.getAllSelectedStrings();
|
||||
} else {
|
||||
approvals = restrictions.getStringArray(KEY_APPROVALS);
|
||||
}
|
||||
String text;
|
||||
if (approvals == null || approvals.length == 0) {
|
||||
text = getString(R.string.none);
|
||||
} else {
|
||||
text = TextUtils.join(", ", approvals);
|
||||
}
|
||||
mTextApprovals.setText(getString(R.string.approvals_you_have, text));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.say_hello: {
|
||||
Toast.makeText(getActivity(), R.string.message_hello, Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), getString(R.string.message, mMessage),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -14,17 +14,24 @@
|
||||
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.example.android.google.wearable.app">
|
||||
package="com.example.android.appusagestatistics"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
|
||||
<uses-sdk android:minSdkVersion="18"
|
||||
android:targetSdkVersion="21" />
|
||||
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
|
||||
|
||||
<application android:allowBackup="true"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:theme="@style/Theme.AppCompat.Light">
|
||||
|
||||
<activity android:name=".AppUsageStatisticsActivity"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
10
samples/browseable/AppUsageStatistics/_index.jd
Normal file
@@ -0,0 +1,10 @@
|
||||
page.tags="AppUsageStatistics"
|
||||
sample.group=System
|
||||
@jd:body
|
||||
|
||||
<p>
|
||||
|
||||
This sample explains how to use App usage statistics API, which was introduced
|
||||
in Android 5.0.
|
||||
|
||||
</p>
|
||||
|
After Width: | Height: | Size: 9.2 KiB |
BIN
samples/browseable/AppUsageStatistics/res/drawable-hdpi/ic_launcher.png
Executable file
|
After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 196 B After Width: | Height: | Size: 196 B |
|
After Width: | Height: | Size: 5.1 KiB |
BIN
samples/browseable/AppUsageStatistics/res/drawable-mdpi/ic_launcher.png
Executable file
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 14 KiB |
BIN
samples/browseable/AppUsageStatistics/res/drawable-xhdpi/ic_launcher.png
Executable file
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 19 KiB |
BIN
samples/browseable/AppUsageStatistics/res/drawable-xxhdpi/ic_launcher.png
Executable file
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 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.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.example.android.appusagestatistics.AppUsageStatisticsActivity"
|
||||
tools:ignore="MergeRootFrame" />
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 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:gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/margin_medium">
|
||||
|
||||
<Button android:id="@+id/button_open_usage_setting"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/open_app_usage_setting"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
<LinearLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
<TextView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/time_span"
|
||||
/>
|
||||
|
||||
<Spinner android:id="@+id/spinner_time_span"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/recyclerview_app_usage"
|
||||
android:layout_marginLeft="@dimen/margin_small"
|
||||
android:layout_marginRight="@dimen/margin_small"
|
||||
android:scrollbars="vertical"
|
||||
android:drawSelectorOnTop="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 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.
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/usage_row_height"
|
||||
>
|
||||
|
||||
<LinearLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center_vertical"
|
||||
>
|
||||
<ImageView android:id="@+id/app_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
<LinearLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/margin_medium"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview_package_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/PackageNameFont"
|
||||
/>
|
||||
|
||||
<LinearLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview_last_time_used_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/last_time_used"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview_last_time_used"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<View android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#bbbbbb"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -15,12 +15,13 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">FragmentTransition</string>
|
||||
<string name="app_name">AppUsageStatistics</string>
|
||||
<string name="intro_message">
|
||||
<![CDATA[
|
||||
|
||||
|
||||
This sample demonstrates how to start a transition right after a fragment transaction.
|
||||
This sample explains how to use App usage statistics API, which was introduced
|
||||
in Android 5.0.
|
||||
|
||||
|
||||
]]>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2013 The Android Open Source Project
|
||||
Copyright 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.
|
||||
@@ -14,7 +14,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
|
||||
<dimen name="usage_row_height">72dp</dimen>
|
||||
</resources>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 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.
|
||||
-->
|
||||
<resources>
|
||||
<string-array name="action_list">
|
||||
<item>Daily</item>
|
||||
<item>Weekly</item>
|
||||
<item>Monthly</item>
|
||||
<item>Yearly</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -14,17 +14,12 @@
|
||||
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.example.android.wearable.flashlight">
|
||||
|
||||
<uses-sdk android:minSdkVersion="18"
|
||||
android:targetSdkVersion="21" />
|
||||
|
||||
<application android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name">
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
<resources>
|
||||
<string name="open_app_usage_setting">Open Apps with usage access settings</string>
|
||||
<string name="last_time_used">"Last time used: "</string>
|
||||
<string name="time_span">"Time span: "</string>
|
||||
<string name="explanation_access_to_appusage_is_not_enabled">Failed to retrieve app usage
|
||||
statistics. You may need to enable access
|
||||
for this app through Settings > Security > Apps with usage access
|
||||
</string>
|
||||
</resources>
|
||||
21
samples/browseable/AppUsageStatistics/res/values/styles.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 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.
|
||||
-->
|
||||
<resources>
|
||||
<style name="PackageNameFont" parent="@android:style/TextAppearance.Small">
|
||||
<item name="android:textColor">#000000</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
package com.example.android.appusagestatistics;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
|
||||
/**
|
||||
* Launcher Activity for the App Usage Statistics sample app.
|
||||
*/
|
||||
public class AppUsageStatisticsActivity extends ActionBarActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_app_usage_statistics);
|
||||
if (savedInstanceState == null) {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.add(R.id.container, AppUsageStatisticsFragment.newInstance())
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
package com.example.android.appusagestatistics;
|
||||
|
||||
import android.app.usage.UsageStats;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.SpinnerAdapter;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Fragment that demonstrates how to use App Usage Statistics API.
|
||||
*/
|
||||
public class AppUsageStatisticsFragment extends Fragment {
|
||||
|
||||
private static final String TAG = AppUsageStatisticsFragment.class.getSimpleName();
|
||||
|
||||
//VisibleForTesting for variables below
|
||||
UsageStatsManager mUsageStatsManager;
|
||||
UsageListAdapter mUsageListAdapter;
|
||||
RecyclerView mRecyclerView;
|
||||
RecyclerView.LayoutManager mLayoutManager;
|
||||
Button mOpenUsageSettingButton;
|
||||
Spinner mSpinner;
|
||||
|
||||
/**
|
||||
* Use this factory method to create a new instance of
|
||||
* this fragment using the provided parameters.
|
||||
*
|
||||
* @return A new instance of fragment {@link AppUsageStatisticsFragment}.
|
||||
*/
|
||||
public static AppUsageStatisticsFragment newInstance() {
|
||||
AppUsageStatisticsFragment fragment = new AppUsageStatisticsFragment();
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public AppUsageStatisticsFragment() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mUsageStatsManager = (UsageStatsManager) getActivity()
|
||||
.getSystemService("usagestats"); //Context.USAGE_STATS_SERVICE
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_app_usage_statistics, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View rootView, Bundle savedInstanceState) {
|
||||
super.onViewCreated(rootView, savedInstanceState);
|
||||
|
||||
mLayoutManager = new LinearLayoutManager(getActivity());
|
||||
mUsageListAdapter = new UsageListAdapter();
|
||||
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview_app_usage);
|
||||
mRecyclerView.setLayoutManager(mLayoutManager);
|
||||
mRecyclerView.scrollToPosition(0);
|
||||
mRecyclerView.setAdapter(mUsageListAdapter);
|
||||
mOpenUsageSettingButton = (Button) rootView.findViewById(R.id.button_open_usage_setting);
|
||||
mSpinner = (Spinner) rootView.findViewById(R.id.spinner_time_span);
|
||||
SpinnerAdapter spinnerAdapter = ArrayAdapter.createFromResource(getActivity(),
|
||||
R.array.action_list, android.R.layout.simple_spinner_dropdown_item);
|
||||
mSpinner.setAdapter(spinnerAdapter);
|
||||
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
|
||||
String[] strings = getResources().getStringArray(R.array.action_list);
|
||||
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
StatsUsageInterval statsUsageInterval = StatsUsageInterval
|
||||
.getValue(strings[position]);
|
||||
if (statsUsageInterval != null) {
|
||||
List<UsageStats> usageStatsList =
|
||||
getUsageStatistics(statsUsageInterval.mInterval);
|
||||
Collections.sort(usageStatsList, new LastTimeLaunchedComparatorDesc());
|
||||
updateAppsList(usageStatsList);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link #mRecyclerView} including the time span specified by the
|
||||
* intervalType argument.
|
||||
*
|
||||
* @param intervalType The time interval by which the stats are aggregated.
|
||||
* Corresponding to the value of {@link UsageStatsManager}.
|
||||
* E.g. {@link UsageStatsManager#INTERVAL_DAILY}, {@link
|
||||
* UsageStatsManager#INTERVAL_WEEKLY},
|
||||
*
|
||||
* @return A list of {@link android.app.usage.UsageStats}.
|
||||
*/
|
||||
public List<UsageStats> getUsageStatistics(int intervalType) {
|
||||
// Get the app statistics since one year ago from the current time.
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.add(Calendar.YEAR, -1);
|
||||
|
||||
List<UsageStats> queryUsageStats = mUsageStatsManager
|
||||
.queryUsageStats(intervalType, cal.getTimeInMillis(),
|
||||
System.currentTimeMillis());
|
||||
|
||||
if (queryUsageStats.size() == 0) {
|
||||
Log.i(TAG, "The user may not allow the access to apps usage. ");
|
||||
Toast.makeText(getActivity(),
|
||||
getString(R.string.explanation_access_to_appusage_is_not_enabled),
|
||||
Toast.LENGTH_LONG).show();
|
||||
mOpenUsageSettingButton.setVisibility(View.VISIBLE);
|
||||
mOpenUsageSettingButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
|
||||
}
|
||||
});
|
||||
}
|
||||
return queryUsageStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the {@link #mRecyclerView} with the list of {@link UsageStats} passed as an argument.
|
||||
*
|
||||
* @param usageStatsList A list of {@link UsageStats} from which update the
|
||||
* {@link #mRecyclerView}.
|
||||
*/
|
||||
//VisibleForTesting
|
||||
void updateAppsList(List<UsageStats> usageStatsList) {
|
||||
List<CustomUsageStats> customUsageStatsList = new ArrayList<>();
|
||||
for (int i = 0; i < usageStatsList.size(); i++) {
|
||||
CustomUsageStats customUsageStats = new CustomUsageStats();
|
||||
customUsageStats.usageStats = usageStatsList.get(i);
|
||||
try {
|
||||
Drawable appIcon = getActivity().getPackageManager()
|
||||
.getApplicationIcon(customUsageStats.usageStats.getPackageName());
|
||||
customUsageStats.appIcon = appIcon;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, String.format("App Icon is not found for %s",
|
||||
customUsageStats.usageStats.getPackageName()));
|
||||
customUsageStats.appIcon = getActivity()
|
||||
.getDrawable(R.drawable.ic_default_app_launcher);
|
||||
}
|
||||
customUsageStatsList.add(customUsageStats);
|
||||
}
|
||||
mUsageListAdapter.setCustomUsageStatsList(customUsageStatsList);
|
||||
mUsageListAdapter.notifyDataSetChanged();
|
||||
mRecyclerView.scrollToPosition(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link Comparator} to sort a collection of {@link UsageStats} sorted by the timestamp
|
||||
* last time the app was used in the descendant order.
|
||||
*/
|
||||
private static class LastTimeLaunchedComparatorDesc implements Comparator<UsageStats> {
|
||||
|
||||
@Override
|
||||
public int compare(UsageStats left, UsageStats right) {
|
||||
return (int) (right.getLastTimeUsed() - left.getLastTimeUsed());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum represents the intervals for {@link android.app.usage.UsageStatsManager} so that
|
||||
* values for intervals can be found by a String representation.
|
||||
*
|
||||
*/
|
||||
//VisibleForTesting
|
||||
static enum StatsUsageInterval {
|
||||
DAILY("Daily", UsageStatsManager.INTERVAL_DAILY),
|
||||
WEEKLY("Weekly", UsageStatsManager.INTERVAL_WEEKLY),
|
||||
MONTHLY("Monthly", UsageStatsManager.INTERVAL_MONTHLY),
|
||||
YEARLY("Yearly", UsageStatsManager.INTERVAL_YEARLY);
|
||||
|
||||
private int mInterval;
|
||||
private String mStringRepresentation;
|
||||
|
||||
StatsUsageInterval(String stringRepresentation, int interval) {
|
||||
mStringRepresentation = stringRepresentation;
|
||||
mInterval = interval;
|
||||
}
|
||||
|
||||
static StatsUsageInterval getValue(String stringRepresentation) {
|
||||
for (StatsUsageInterval statsUsageInterval : values()) {
|
||||
if (statsUsageInterval.mStringRepresentation.equals(stringRepresentation)) {
|
||||
return statsUsageInterval;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
package com.example.android.appusagestatistics;
|
||||
|
||||
import android.app.usage.UsageStats;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* Entity class represents usage stats and app icon.
|
||||
*/
|
||||
public class CustomUsageStats {
|
||||
public UsageStats usageStats;
|
||||
public Drawable appIcon;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.android.appusagestatistics;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provide views to RecyclerView with the directory entries.
|
||||
*/
|
||||
public class UsageListAdapter extends RecyclerView.Adapter<UsageListAdapter.ViewHolder> {
|
||||
|
||||
private List<CustomUsageStats> mCustomUsageStatsList = new ArrayList<>();
|
||||
private DateFormat mDateFormat = new SimpleDateFormat();
|
||||
|
||||
/**
|
||||
* Provide a reference to the type of views that you are using (custom ViewHolder)
|
||||
*/
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private final TextView mPackageName;
|
||||
private final TextView mLastTimeUsed;
|
||||
private final ImageView mAppIcon;
|
||||
|
||||
public ViewHolder(View v) {
|
||||
super(v);
|
||||
mPackageName = (TextView) v.findViewById(R.id.textview_package_name);
|
||||
mLastTimeUsed = (TextView) v.findViewById(R.id.textview_last_time_used);
|
||||
mAppIcon = (ImageView) v.findViewById(R.id.app_icon);
|
||||
}
|
||||
|
||||
public TextView getLastTimeUsed() {
|
||||
return mLastTimeUsed;
|
||||
}
|
||||
|
||||
public TextView getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
public ImageView getAppIcon() {
|
||||
return mAppIcon;
|
||||
}
|
||||
}
|
||||
|
||||
public UsageListAdapter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
|
||||
View v = LayoutInflater.from(viewGroup.getContext())
|
||||
.inflate(R.layout.usage_row, viewGroup, false);
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
|
||||
viewHolder.getPackageName().setText(
|
||||
mCustomUsageStatsList.get(position).usageStats.getPackageName());
|
||||
long lastTimeUsed = mCustomUsageStatsList.get(position).usageStats.getLastTimeUsed();
|
||||
viewHolder.getLastTimeUsed().setText(mDateFormat.format(new Date(lastTimeUsed)));
|
||||
viewHolder.getAppIcon().setImageDrawable(mCustomUsageStatsList.get(position).appIcon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mCustomUsageStatsList.size();
|
||||
}
|
||||
|
||||
public void setCustomUsageStatsList(List<CustomUsageStats> customUsageStats) {
|
||||
mCustomUsageStatsList = customUsageStats;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -20,7 +20,7 @@
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.example.android.basicmanagedprofile.MainActivity.MainFragment">
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 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">
|
||||
|
||||
<LinearLayout style="@style/Widget.SampleMessageTile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Widget.SampleMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/horizontal_page_margin"
|
||||
android:layout_marginRight="@dimen/horizontal_page_margin"
|
||||
android:layout_marginTop="@dimen/vertical_page_margin"
|
||||
android:layout_marginBottom="@dimen/vertical_page_margin"
|
||||
android:text="@string/intro_message" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -4,9 +4,9 @@ sample.group=Input
|
||||
|
||||
<p>
|
||||
|
||||
This samples demonstrates the use of MotionEvent properties to keep track of individual touches
|
||||
This sample demonstrates the use of MotionEvent properties to keep track of individual touches
|
||||
across multiple touch events.
|
||||
\n\nTouch the screen with multiple fingers to show that the pointer id
|
||||
(also represented by a colour) does not change as new touch events are received.</string>
|
||||
(also represented by a color) does not change as new touch events are received.
|
||||
|
||||
</p>
|
||||
|
||||