1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.widget.cts.appwidget; 18 19 import android.app.PendingIntent; 20 import android.appwidget.AppWidgetManager; 21 import android.appwidget.AppWidgetProvider; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.net.Uri; 25 import android.os.Handler; 26 import android.os.Looper; 27 import android.view.View; 28 import android.widget.RemoteViews; 29 import android.widget.cts.R; 30 31 import com.android.compatibility.common.util.PollingCheck; 32 33 import java.util.concurrent.CountDownLatch; 34 35 public final class MyAppWidgetProvider extends AppWidgetProvider { 36 private static final long TIME_SLICE = 100; 37 38 public static final String KEY_DISPLAYED_CHILD_INDEX = 39 "MyAppWidgetProvider.displayedChildIndex"; 40 public static final String KEY_SHOW_NEXT = "MyAppWidgetProvider.showNext"; 41 public static final String KEY_SHOW_PREVIOUS = "MyAppWidgetProvider.showPrevious"; 42 public static final String KEY_SWITCH_TO_LIST = "MyAppWidgetProvider.switchToList"; 43 public static final String KEY_SCROLL_POSITION = "MyAppWidgetProvider.scrollPosition"; 44 public static final String KEY_SCROLL_OFFSET = "MyAppWidgetProvider.scrollOffset"; 45 46 // This latch will be notified when onEnabled is called on our provider. 47 private static CountDownLatch sCountDownLatch; 48 // Gating condition to be polled to proceed with setScrollPosition call. 49 private static PollingCheck.PollingCheckCondition sSetScrollCondition; 50 // Gating condition to be polled to proceed with setRelativeScrollPosition call. 51 private static PollingCheck.PollingCheckCondition sSetRelativeScrollCondition; 52 53 private int mDisplayedChildIndex; 54 private boolean mShowNext; 55 private boolean mShowPrevious; 56 private boolean mSwitchToList; 57 private int mScrollPosition; 58 private int mScrollOffset; 59 60 public static void configure(CountDownLatch countDownLatch, 61 PollingCheck.PollingCheckCondition setScrollCondition, 62 PollingCheck.PollingCheckCondition setRelativeScrollCondition) { 63 sCountDownLatch = countDownLatch; 64 sSetScrollCondition = setScrollCondition; 65 sSetRelativeScrollCondition = setRelativeScrollCondition; 66 } 67 68 @Override 69 public void onReceive(Context context, Intent intent) { 70 mDisplayedChildIndex = intent.getIntExtra(KEY_DISPLAYED_CHILD_INDEX, -1); 71 mShowNext = intent.getBooleanExtra(KEY_SHOW_NEXT, false); 72 mShowPrevious = intent.getBooleanExtra(KEY_SHOW_PREVIOUS, false); 73 mSwitchToList = intent.getBooleanExtra(KEY_SWITCH_TO_LIST, false); 74 mScrollPosition = intent.getIntExtra(KEY_SCROLL_POSITION, -1); 75 mScrollOffset = intent.getIntExtra(KEY_SCROLL_OFFSET, 0); 76 77 super.onReceive(context, intent); 78 } 79 80 @Override 81 public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { 82 final int appWidgetId = appWidgetIds[0]; 83 final RemoteViews widgetAdapterView = new RemoteViews(context.getPackageName(), 84 R.layout.remoteviews_adapter); 85 86 final Intent stackIntent = new Intent(context, MyAppWidgetService.class); 87 stackIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 88 stackIntent.setData(Uri.parse(stackIntent.toUri(Intent.URI_INTENT_SCHEME))); 89 90 widgetAdapterView.setRemoteAdapter(R.id.remoteViews_stack, stackIntent); 91 widgetAdapterView.setEmptyView(R.id.remoteViews_stack, R.id.remoteViews_empty); 92 93 if (mDisplayedChildIndex >= 0) { 94 widgetAdapterView.setDisplayedChild(R.id.remoteViews_stack, mDisplayedChildIndex); 95 } 96 if (mShowNext) { 97 widgetAdapterView.showNext(R.id.remoteViews_stack); 98 } 99 if (mShowPrevious) { 100 widgetAdapterView.showPrevious(R.id.remoteViews_stack); 101 } 102 103 // Here we setup the a pending intent template. Individuals items of a collection 104 // cannot setup their own pending intents, instead, the collection as a whole can 105 // setup a pending intent template, and the individual items can set a fillInIntent 106 // to create unique before on an item to item basis. 107 Intent viewIntent = new Intent(Intent.ACTION_VIEW, 108 Uri.parse("ctstest://RemoteView/testWidget")); 109 PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, viewIntent, 110 PendingIntent.FLAG_UPDATE_CURRENT); 111 112 widgetAdapterView.setPendingIntentTemplate(R.id.remoteViews_stack, pendingIntent); 113 114 if (mSwitchToList) { 115 final Intent listIntent = new Intent(context, MyAppWidgetService.class); 116 listIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 117 listIntent.setData(Uri.parse(stackIntent.toUri(Intent.URI_INTENT_SCHEME))); 118 119 widgetAdapterView.setRemoteAdapter(R.id.remoteViews_list, listIntent); 120 121 widgetAdapterView.setViewVisibility(R.id.remoteViews_stack, View.GONE); 122 widgetAdapterView.setViewVisibility(R.id.remoteViews_list, View.VISIBLE); 123 } 124 125 final Handler handler = new Handler(Looper.myLooper()); 126 if (mScrollPosition >= 0) { 127 // We need to schedule the call to setScrollPosition as a separate event that runs 128 // after the underlying ListView has been laid out on the screen. Otherwise calling 129 // that API on a ListView with 0x0 dimension has no effect - the list content is only 130 // populated via the adapter when ListView has "real" bounds. 131 final Runnable setScrollRunnable = new Runnable() { 132 public void run() { 133 if (sSetScrollCondition.canProceed()) { 134 // Gating condition has been satisfied. Call setScrollPosition and 135 // ask the widget manager to update our widget 136 widgetAdapterView.setScrollPosition(R.id.remoteViews_list, mScrollPosition); 137 appWidgetManager.partiallyUpdateAppWidget(appWidgetId, widgetAdapterView); 138 } else { 139 // Keep on "waiting" until the gating condition is satisfied 140 handler.postDelayed(this, TIME_SLICE); 141 } 142 } 143 }; 144 handler.postDelayed(setScrollRunnable, TIME_SLICE); 145 } 146 147 if (mScrollOffset != 0) { 148 // We need to schedule the call to setRelativeScrollPosition as a separate event that 149 // runs after the underlying ListView has been laid out on the screen. Otherwise calling 150 // that API on a ListView with 0x0 dimension has no effect - the list content is only 151 // populated via the adapter when ListView has "real" bounds. 152 final Runnable setRelativeScrollRunnable = new Runnable() { 153 public void run() { 154 if (sSetRelativeScrollCondition.canProceed()) { 155 // Gating condition has been satisfied. Call setRelativeScrollPosition and 156 // ask the widget manager to update our widget 157 widgetAdapterView.setRelativeScrollPosition( 158 R.id.remoteViews_list, mScrollOffset); 159 appWidgetManager.partiallyUpdateAppWidget(appWidgetId, widgetAdapterView); 160 } else { 161 // Keep on "waiting" until the gating condition is satisfied 162 handler.postDelayed(this, TIME_SLICE); 163 } 164 } 165 }; 166 handler.postDelayed(setRelativeScrollRunnable, TIME_SLICE); 167 } 168 169 appWidgetManager.updateAppWidget(appWidgetId, widgetAdapterView); 170 171 sCountDownLatch.countDown(); 172 } 173 174 @Override 175 public void onEnabled(Context context) { 176 sCountDownLatch.countDown(); 177 } 178 } 179