1 /*
2  * Copyright (C) 2019 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 com.android.test.taskembed;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
20 
21 import android.app.Activity;
22 import android.app.ActivityManager;
23 import android.app.ActivityOptions;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.graphics.Color;
27 import android.graphics.Rect;
28 import android.os.Bundle;
29 import android.view.Gravity;
30 import android.view.MotionEvent;
31 import android.view.SurfaceControl;
32 import android.view.SurfaceHolder;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.widget.LinearLayout;
36 import android.window.TaskOrganizer;
37 import android.window.WindowContainerTransaction;
38 import android.window.WindowContainerTransactionCallback;
39 
40 public class TaskOrganizerMultiWindowTest extends Activity {
41     static class SplitLayout extends LinearLayout implements View.OnTouchListener {
42         View mView1;
43         View mView2;
44         View mDividerView;
45 
onTouch(View v, MotionEvent e)46         public boolean onTouch(View v, MotionEvent e) {
47             if (e.getAction() != MotionEvent.ACTION_UP) {
48                 return true;
49             }
50 
51             float x = e.getRawX(0);
52             float ratio = (float) x / (float) getWidth() ;
53             ratio = 1-ratio;
54 
55             LinearLayout.LayoutParams lp1 =
56                 new LinearLayout.LayoutParams(0,
57                         ViewGroup.LayoutParams.WRAP_CONTENT, ratio-0.02f);
58             LinearLayout.LayoutParams lp2 =
59                 new LinearLayout.LayoutParams(0,
60                         ViewGroup.LayoutParams.WRAP_CONTENT, 1-ratio-0.02f);
61             updateViewLayout(mView1, lp2);
62             updateViewLayout(mView2, lp1);
63             return true;
64         }
65 
SplitLayout(Context c, View v1, View v2)66         SplitLayout(Context c, View v1, View v2) {
67             super(c);
68             LinearLayout.LayoutParams lp1 =
69                 new LinearLayout.LayoutParams(0,
70                         ViewGroup.LayoutParams.WRAP_CONTENT, 0.48f);
71             LinearLayout.LayoutParams lp3 =
72                 new LinearLayout.LayoutParams(0,
73                         ViewGroup.LayoutParams.WRAP_CONTENT, 0.48f);
74             LinearLayout.LayoutParams lp2 =
75                 new LinearLayout.LayoutParams(0,
76                         ViewGroup.LayoutParams.FILL_PARENT, 0.04f);
77             lp2.gravity = Gravity.CENTER;
78 
79             setWeightSum(1);
80 
81             mView1 = v1;
82             mView2 = v2;
83             addView(mView1, lp1);
84 
85             mDividerView = new View(getContext());
86             mDividerView.setBackgroundColor(Color.BLACK);
87             addView(mDividerView, lp2);
88             mDividerView.setOnTouchListener(this);
89 
90             addView(mView2, lp3);
91         }
92     }
93 
94     class ResizingTaskView extends TaskView {
95         final Intent mIntent;
96         boolean launched = false;
ResizingTaskView(Context c, Intent i)97         ResizingTaskView(Context c, Intent i) {
98             super(c);
99             mIntent = i;
100         }
101 
102         @Override
surfaceChanged(SurfaceHolder h, int format, int width, int height)103         public void surfaceChanged(SurfaceHolder h, int format, int width, int height) {
104             if (!launched) {
105                 launchOrganizedActivity(mIntent, width, height);
106                 launched = true;
107             } else {
108                 resizeTask(width, height);
109             }
110         }
111 
resizeTask(int width, int height)112         void resizeTask(int width, int height) {
113             final WindowContainerTransaction wct = new WindowContainerTransaction();
114             wct.setBounds(mWc, new Rect(0, 0, width, height));
115             try {
116                 mOrganizer.applySyncTransaction(wct, mOrganizer.mTransactionCallback);
117             } catch (Exception e) {
118                 // Oh well
119             }
120         }
121     }
122 
123     private TaskView mTaskView1;
124     private TaskView mTaskView2;
125     private boolean mGotFirstTask = false;
126 
127     class Organizer extends TaskOrganizer {
128         private int receivedTransactions = 0;
129         SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction();
130         WindowContainerTransactionCallback mTransactionCallback =
131                 new WindowContainerTransactionCallback() {
132             @Override
133             public void onTransactionReady(int id, SurfaceControl.Transaction t) {
134                 mergedTransaction.merge(t);
135                 receivedTransactions++;
136                 if (receivedTransactions == 2) {
137                     mergedTransaction.apply();
138                     receivedTransactions = 0;
139                 }
140             }
141         };
142 
143         @Override
onTaskAppeared(ActivityManager.RunningTaskInfo ti, SurfaceControl leash)144         public void onTaskAppeared(ActivityManager.RunningTaskInfo ti, SurfaceControl leash) {
145             if (!mGotFirstTask) {
146                 mTaskView1.reparentTask(ti.token, leash);
147                 mGotFirstTask = true;
148             } else {
149                 mTaskView2.reparentTask(ti.token, leash);
150             }
151         }
152     }
153 
154     private Organizer mOrganizer = new Organizer();
155 
156     @Override
onCreate(Bundle savedInstanceState)157     protected void onCreate(Bundle savedInstanceState) {
158         super.onCreate(savedInstanceState);
159 
160         mOrganizer.registerOrganizer(WINDOWING_MODE_MULTI_WINDOW);
161 
162         mTaskView1 = new ResizingTaskView(this, makeSettingsIntent());
163         mTaskView2 = new ResizingTaskView(this, makeContactsIntent());
164         View splitView = new SplitLayout(this, mTaskView1, mTaskView2);
165 
166         setContentView(splitView);
167     }
168 
169     @Override
onDestroy()170     protected void onDestroy() {
171         super.onDestroy();
172         mOrganizer.unregisterOrganizer();
173     }
174 
addFlags(Intent intent)175     private void addFlags(Intent intent) {
176         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
177     }
178 
makeSettingsIntent()179     private Intent makeSettingsIntent() {
180         Intent intent = new Intent();
181         intent.setAction(android.provider.Settings.ACTION_SETTINGS);
182         addFlags(intent);
183         return intent;
184     }
185 
makeContactsIntent()186     private Intent makeContactsIntent() {
187         Intent intent = new Intent();
188         intent.setAction(Intent.ACTION_MAIN);
189         intent.addCategory(Intent.CATEGORY_APP_CONTACTS);
190         addFlags(intent);
191         return intent;
192     }
193 
makeLaunchOptions(int width, int height)194     private Bundle makeLaunchOptions(int width, int height) {
195         ActivityOptions o = ActivityOptions.makeBasic();
196         o.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
197         o.setLaunchBounds(new Rect(0, 0, width, height));
198         return o.toBundle();
199     }
200 
launchOrganizedActivity(Intent i, int width, int height)201     private void launchOrganizedActivity(Intent i, int width, int height) {
202         startActivity(i, makeLaunchOptions(width, height));
203     }
204 }
205