Project

General

Profile

« Previous | Next » 

Revision 3a9d19ed

Added by Leszek Koltunski about 4 years ago

Progress with Pretty Patterns.

View differences:

src/main/java/org/distorted/dialog/RubikDialogPattern.java
1 1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 Leszek Koltunski                                                               //
2
// Copyright 2020 Leszek Koltunski                                                               //
3 3
//                                                                                               //
4 4
// This file is part of Magic Cube.                                                              //
5 5
//                                                                                               //
......
21 21

  
22 22
import android.app.Dialog;
23 23
import android.content.Context;
24
import android.content.DialogInterface;
25 24
import android.os.Bundle;
26 25
import android.support.annotation.NonNull;
27 26
import android.support.v4.app.FragmentActivity;
src/main/java/org/distorted/dialog/RubikDialogPatternPagerAdapter.java
37 37
  private RubikDialogPattern mDialog;
38 38
  private int mNumTabs;
39 39

  
40
///////////////////////////////////////////////////////////////////////////////////////////////////
41

  
42
  private String[] createCategories(int pos)
43
    {
44
    RubikPattern pattern = RubikPattern.getInstance();
45
    int numCat = pattern.getNumCategories(pos);
46

  
47
    String[] ret = new String[numCat];
48

  
49
    for(int i=0; i<numCat; i++)
50
      {
51
      ret[i] = pattern.getCategoryName(pos,i);
52
      }
53

  
54
    return ret;
55
    }
56

  
57 40
///////////////////////////////////////////////////////////////////////////////////////////////////
58 41

  
59 42
  RubikDialogPatternPagerAdapter(FragmentActivity act, ViewPager viewPager, RubikDialogPattern dialog)
......
73 56
  @NonNull
74 57
  public Object instantiateItem(@NonNull ViewGroup collection, final int position)
75 58
    {
76
    String[] categories = createCategories(position);
77
    mViews[position] = new RubikDialogPatternView(mAct, mDialog, categories);
59
    mViews[position] = new RubikDialogPatternView(mAct, mDialog, position);
78 60
    collection.addView(mViews[position]);
79 61

  
80 62
    return mViews[position];
src/main/java/org/distorted/dialog/RubikDialogPatternView.java
24 24
import android.util.AttributeSet;
25 25
import android.util.DisplayMetrics;
26 26
import android.view.View;
27
import android.widget.AdapterView;
28
import android.widget.ArrayAdapter;
27 29
import android.widget.Button;
28 30
import android.widget.FrameLayout;
29 31
import android.widget.LinearLayout;
32
import android.widget.Spinner;
30 33

  
31 34
import org.distorted.magic.R;
32 35
import org.distorted.magic.RubikActivity;
36
import org.distorted.patterns.RubikPattern;
33 37
import org.distorted.uistate.RubikState;
38
import org.distorted.uistate.RubikStatePattern;
34 39

  
35 40
///////////////////////////////////////////////////////////////////////////////////////////////////
36 41

  
37
public class RubikDialogPatternView extends FrameLayout
42
public class RubikDialogPatternView extends FrameLayout implements AdapterView.OnItemSelectedListener
38 43
  {
39 44
  private LinearLayout mLayout;
40 45
  private RubikDialogPattern mDialog;
46
  private int mPosition;
41 47

  
42 48
///////////////////////////////////////////////////////////////////////////////////////////////////
43 49

  
......
55 61

  
56 62
///////////////////////////////////////////////////////////////////////////////////////////////////
57 63

  
58
  public RubikDialogPatternView(FragmentActivity act, RubikDialogPattern dialog, String[] categories)
64
  public RubikDialogPatternView(FragmentActivity act, RubikDialogPattern dialog, int position)
59 65
    {
60 66
    super(act);
61 67

  
62 68
    mDialog = dialog;
69
    mPosition = position;
63 70
    View tab = inflate( act, R.layout.dialog_pattern_tab, null);
64 71
    mLayout = tab.findViewById(R.id.tabLayout);
65
    createSection(act,categories);
72

  
73
    String[] categories = createCategories();
74

  
75
    Spinner spinner = tab.findViewById(R.id.pattern_category_spinner);
76
    spinner.setOnItemSelectedListener(this);
77

  
78
    ArrayAdapter<String> adapter = new ArrayAdapter<>(act, android.R.layout.simple_spinner_item, categories);
79
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
80
    spinner.setAdapter(adapter);
81

  
82
    spinner.setSelection(0);
66 83

  
67 84
    addView(tab);
68 85
    }
69 86

  
70 87
///////////////////////////////////////////////////////////////////////////////////////////////////
71 88

  
72
  private void createSection(final FragmentActivity act, final String[] categories)
89
  private String[] createCategories()
90
    {
91
    RubikPattern pattern = RubikPattern.getInstance();
92
    int numCat = pattern.getNumCategories(mPosition);
93

  
94
    String[] ret = new String[numCat];
95

  
96
    for(int i=0; i<numCat; i++)
97
      {
98
      ret[i] = pattern.getCategoryName(mPosition,i);
99
      }
100

  
101
    return ret;
102
    }
103

  
104
///////////////////////////////////////////////////////////////////////////////////////////////////
105

  
106
  private String[] createPatterns(int category)
73 107
    {
108
    RubikPattern pattern = RubikPattern.getInstance();
109
    int numPat = pattern.getNumPatterns(mPosition, category);
110

  
111
    String[] ret = new String[numPat];
112

  
113
    for(int i=0; i<numPat; i++)
114
      {
115
      ret[i] = pattern.getPatternName(mPosition, category, i);
116
      }
117

  
118
    return ret;
119
    }
120

  
121
///////////////////////////////////////////////////////////////////////////////////////////////////
122

  
123
  private void fillPatterns(int category)
124
    {
125
    final RubikActivity act = (RubikActivity)getContext();
126

  
74 127
    DisplayMetrics metrics = act.getResources().getDisplayMetrics();
75 128
    final float scale = metrics.density;
76 129
    int margin = (int)(3*scale + 0.5f);
77 130
    LinearLayout.LayoutParams bParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
78 131
    bParams.setMargins(margin, margin, margin, margin);
79 132

  
80
    final RubikActivity ract = (RubikActivity)getContext();
133
    final String[] patterns = createPatterns(category);
134
    int len = patterns.length;
81 135

  
82
    for(String category: categories)
136
    mLayout.removeAllViews();
137

  
138
    for(int i=0; i<len; i++)
83 139
      {
140
      final int ii = i;
84 141
      Button button = new Button(act);
85 142
      button.setLayoutParams(bParams);
86
      button.setText(category);
143
      button.setText(patterns[i]);
87 144

  
88 145
      button.setOnClickListener( new View.OnClickListener()
89 146
        {
90 147
        @Override
91 148
        public void onClick(View view)
92 149
          {
93
          RubikState.switchState(ract,RubikState.PATT);
150
          RubikStatePattern state = (RubikStatePattern) RubikState.PATT.getStateClass();
151
          state.setPatternName(patterns[ii]);
94 152
          mDialog.dismiss();
95 153
          }
96 154
        });
......
98 156
      mLayout.addView(button);
99 157
      }
100 158
    }
159

  
160
///////////////////////////////////////////////////////////////////////////////////////////////////
161

  
162
  @Override
163
  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
164
    {
165
    if( parent.getId() == R.id.pattern_category_spinner )
166
      {
167
      fillPatterns(pos);
168
      }
169
    }
170

  
171
///////////////////////////////////////////////////////////////////////////////////////////////////
172

  
173
  @Override
174
  public void onNothingSelected(AdapterView<?> parent)
175
    {
176

  
177
    }
101 178
  }
src/main/java/org/distorted/patterns/RubikPattern.java
41 41

  
42 42
///////////////////////////////////////////////////////////////////////////////////////////////////
43 43

  
44
  private class Category
44
  private static class Category
45 45
    {
46 46
    private String name;
47
    private int numpatterns;
47
    private int numPatterns;
48 48
    private Vector<Pattern> patterns;
49
    private int curPattern;
49

  
50
  /////////////////////////////////////////////////////////////
50 51

  
51 52
    Category(String n)
52 53
      {
53 54
      name=n;
54
      curPattern=0;
55
      numpatterns=0;
55
      numPatterns=0;
56 56
      patterns = new Vector<>();
57 57
      }
58

  
59
  /////////////////////////////////////////////////////////////
60

  
58 61
    void addPattern(Pattern p)
59 62
      {
60 63
      patterns.addElement(p);
61
      numpatterns++;
64
      numPatterns++;
62 65
      }
66

  
67
  /////////////////////////////////////////////////////////////
68

  
63 69
    int getNumPatterns()
64 70
    {
65
    return numpatterns;
71
    return numPatterns;
66 72
    }
73

  
74
  /////////////////////////////////////////////////////////////
75

  
67 76
    String getName()
68 77
    {
69 78
    return name;
70 79
    }
71
    void next()
72
      {
73
      curPattern++;
74
      if( curPattern>=numpatterns )
75
        curPattern=0;
76
      }
77
    void prev()
78
      {
79
      curPattern--;
80
      if( curPattern<0 )
81
        curPattern=numpatterns-1;
82
      }
83
    String getPatternName()
80

  
81
  /////////////////////////////////////////////////////////////
82

  
83
    String getPatternName(int pattern)
84 84
      {
85
      Pattern p = patterns.elementAt(curPattern);
86
      return  p!=null ? p.getName(curPattern+1,numpatterns):null;
85
      if( pattern>=0 && pattern<numPatterns )
86
        {
87
        Pattern p = patterns.elementAt(pattern);
88
        return  p!=null ? p.getName():"";
89
        }
90
      return "";
87 91
      }
88
    int getPatternCurMove()
92

  
93
  /////////////////////////////////////////////////////////////
94

  
95
    int getPatternCurMove(int pattern)
89 96
      {
90
      Pattern p = patterns.elementAt(curPattern);
91
      return  p!=null ? p.getCurMove():-1;
97
      if( pattern>=0 && pattern<numPatterns )
98
        {
99
        Pattern p = patterns.elementAt(pattern);
100
        return  p!=null ? p.getCurMove():-1;
101
        }
102
      return -1;
92 103
      }
93
    int getPatternNumMove()
104

  
105
  /////////////////////////////////////////////////////////////
106

  
107
    int getPatternNumMove(int pattern)
94 108
      {
95
      Pattern p = patterns.elementAt(curPattern);
96
      return  p!=null ? p.getNumMove():-1;
109
      if( pattern>=0 && pattern<numPatterns )
110
        {
111
        Pattern p = patterns.elementAt(pattern);
112
        return  p!=null ? p.getNumMove():-1;
113
        }
114
      return -1;
97 115
      }
98
    void initializeCube()
116

  
117
  /////////////////////////////////////////////////////////////
118

  
119
    String retInitializationString(int pattern)
99 120
      {
100
      Pattern p = patterns.elementAt(curPattern);
101
      if( p!=null ) p.initializeCube();
121
      if( pattern>=0 && pattern<numPatterns )
122
        {
123
        Pattern p = patterns.elementAt(pattern);
124
        if( p!=null ) return p.retInitializationString();
125
        }
126

  
127
      return "";
102 128
      }
103
    void makeNextMove()
129

  
130
  /////////////////////////////////////////////////////////////
131

  
132
    String retNextMove(int pattern)
104 133
      {
105
      Pattern p = patterns.elementAt(curPattern);
106
      if( p!=null ) p.makeNextMove();
134
      if( pattern>=0 && pattern<numPatterns )
135
        {
136
        Pattern p = patterns.elementAt(pattern);
137
        if( p!=null ) return p.retNextMove();
138
        }
139

  
140
      return "";
107 141
      }
108
    void makePrevMove()
142

  
143
  /////////////////////////////////////////////////////////////
144

  
145
    String retPrevMove(int pattern)
109 146
      {
110
      Pattern p = patterns.elementAt(curPattern);
111
      if( p!=null ) p.makePrevMove();
147
      if( pattern>=0 && pattern<numPatterns )
148
        {
149
        Pattern p = patterns.elementAt(pattern);
150
        if( p!=null ) return p.retPrevMove();
151
        }
152

  
153
      return "";
112 154
      }
113 155
    }
114 156

  
115 157
///////////////////////////////////////////////////////////////////////////////////////////////////
116 158

  
117
  private class Pattern
159
  private static class Pattern
118 160
    {
119 161
    private String name;
120 162
    private String shortname=null;
......
122 164
    private int curmove;
123 165
    private int nummove;
124 166

  
167
  /////////////////////////////////////////////////////////////
168

  
125 169
    Pattern(String n, String m)
126 170
      {
127 171
      name=n;
......
129 173
      nummove = moves.length()/4;
130 174
      curmove=nummove;
131 175
      }
132
    String getName(int cur,int num)
176

  
177
  /////////////////////////////////////////////////////////////
178

  
179
    String getName()
133 180
      {
134 181
      if( shortname==null ) shortname=cutPatternName(name);
135 182
      return shortname;
136 183
      }
184

  
185
  /////////////////////////////////////////////////////////////
186

  
137 187
    int getNumMove()
138 188
    {
139 189
    return nummove;
140 190
    }
191

  
192
  /////////////////////////////////////////////////////////////
193

  
141 194
    int getCurMove()
142 195
    {
143 196
    return curmove;
144 197
    }
145
    void makeNextMove()
198

  
199
  /////////////////////////////////////////////////////////////
200

  
201
    String retNextMove()
146 202
      {
147 203
      curmove++;
204

  
148 205
      if( curmove>nummove)
149 206
        {
150 207
        curmove= 0;
151
        initializeCube();
208
        return retInitializationString();
152 209
        }
153 210
      else
154 211
        {
155
// TODO
156
//        RubikCube cube = world.getCube();
157
//        if( cube!=null && !cube.makeMove(moves.substring(4*curmove-4,4*curmove)) )
158
          curmove--;
212
        curmove--;
213
        return moves.substring(4*curmove-4,4*curmove);
159 214
        }
160 215
      }
161
    void makePrevMove()
216

  
217
  /////////////////////////////////////////////////////////////
218

  
219
    String retPrevMove()
162 220
      {
163 221
      curmove--;
222

  
164 223
      if( curmove<0)
165 224
        {
166 225
        curmove=nummove;
167
        initializeCube();
226
        return retInitializationString();
168 227
        }
169 228
      else
170 229
        {
171
// TODO
172
//        RubikCube cube = world.getCube();
173
//        if( cube!=null && !cube.backMove(moves.substring(4*curmove,4*curmove+4)) )
174
          curmove++;
230
        curmove++;
231
        return moves.substring(4*curmove,4*curmove+4);
175 232
        }
176 233
      }
177
    void initializeCube()
234

  
235
  /////////////////////////////////////////////////////////////
236

  
237
    String retInitializationString()
178 238
      {
179
// TODO
180
//	    RubikCube cube = world.getCube();
181
//      if( cube!=null ) cube.setupPosition(moves.substring(0,4*curmove));
239
      return moves.substring(0,4*curmove);
182 240
      }
183 241
    }
184 242

  
......
187 245
  private RubikPattern()
188 246
    {
189 247
    mPaint.setTextSize(24);
190
    mWidth = 200;
248
    mWidth = 300;
191 249
    mCategories = new Vector[NUM_CUBES];
192 250

  
193 251
    initializeCategories(0, RubikPatternData2.patterns);
......
265 323
    return mThis;
266 324
    }
267 325

  
326
///////////////////////////////////////////////////////////////////////////////////////////////////
327

  
328
  public int getNumCategories(int size)
329
    {
330
    return size>=0 && size<NUM_CUBES ? numCategories[size] : 0;
331
    }
332

  
268 333
///////////////////////////////////////////////////////////////////////////////////////////////////
269 334

  
270 335
  public String getCategoryName(int size,int num)
......
280 345

  
281 346
///////////////////////////////////////////////////////////////////////////////////////////////////
282 347

  
283
  public String getPatternName(int size,int num)
348
  public String getPatternName(int size, int cat, int pat)
284 349
    {
285
    if( size>=0 && size<NUM_CUBES && num>=0 && num< numCategories[size] )
350
    if( size>=0 && size<NUM_CUBES && cat>=0 && cat< numCategories[size] )
286 351
      {
287
      Category c = mCategories[size].elementAt(num);
288
      return c!=null ? c.getPatternName() : null;
352
      Category c = mCategories[size].elementAt(cat);
353
      return c!=null ? c.getPatternName(pat) : null;
289 354
      }
290 355

  
291 356
    return null;
......
306 371

  
307 372
///////////////////////////////////////////////////////////////////////////////////////////////////
308 373

  
309
  public void nextPattern(int size, int num )
310
    {
311
    if( size>=0 && size<NUM_CUBES && num>=0 && num< numCategories[size] )
312
      {
313
      Category c = mCategories[size].elementAt(num);
314

  
315
      if( c!=null )
316
        {
317
// TODO
318
//        RubikMenu.texDirty[0] = true;
319
        c.next();
320
        c.initializeCube();
321
        }
322
      }
323
    }
324

  
325
///////////////////////////////////////////////////////////////////////////////////////////////////
326

  
327
  public void prevPattern(int size, int num )
328
    {
329
    if( size>=0 && size<NUM_CUBES && num>=0 && num< numCategories[size] )
330
      {
331
      Category c = mCategories[size].elementAt(num);
332

  
333
      if( c!=null )
334
        {
335
// TODO
336
//        RubikMenu.texDirty[0] = true;
337
        c.prev();
338
        c.initializeCube();
339
        }
340
      }
341
    }
342

  
343
///////////////////////////////////////////////////////////////////////////////////////////////////
344

  
345
  public int getCurrPattern(int size, int num )
374
  public int getCurMove(int size, int cat, int pat)
346 375
    {
347
    if( size>=0 && size<NUM_CUBES && num>=0 && num< numCategories[size] )
376
    if( size>=0 && size<NUM_CUBES && cat>=0 && cat< numCategories[size] )
348 377
      {
349
      Category c = mCategories[size].elementAt(num);
350
      return c!=null ? c.curPattern:0;
378
      Category c = mCategories[size].elementAt(cat);
379
      return c!=null ? c.getPatternCurMove(pat):0;
351 380
      }
352 381

  
353 382
    return 0;
......
355 384

  
356 385
///////////////////////////////////////////////////////////////////////////////////////////////////
357 386

  
358
  public int getCurMove(int size, int num )
387
  public int getNumMoves(int size, int cat, int pat)
359 388
    {
360
    if( size>=0 && size<NUM_CUBES && num>=0 && num< numCategories[size] )
389
    if( size>=0 && size<NUM_CUBES && cat>=0 && cat< numCategories[size] )
361 390
      {
362
      Category c = mCategories[size].elementAt(num);
363
      return c!=null ? c.getPatternCurMove():0;
391
      Category c = mCategories[size].elementAt(cat);
392
      return c!=null ? c.getPatternNumMove(pat):0;
364 393
      }
365 394

  
366 395
    return 0;
......
368 397

  
369 398
///////////////////////////////////////////////////////////////////////////////////////////////////
370 399

  
371
  public int getNumMove(int size, int num )
400
  public String retNextMove(int size, int cat, int pat)
372 401
    {
373
    if( size>=0 && size<NUM_CUBES && num>=0 && num< numCategories[size] )
402
    if( size>=0 && size<NUM_CUBES && cat>=0 && cat< numCategories[size] )
374 403
      {
375
      Category c = mCategories[size].elementAt(num);
376
      return c!=null ? c.getPatternNumMove():0;
404
      Category c = mCategories[size].elementAt(cat);
405
      if( c!=null ) return c.retNextMove(pat);
377 406
      }
378 407

  
379
    return 0;
408
    return "";
380 409
    }
381 410

  
382 411
///////////////////////////////////////////////////////////////////////////////////////////////////
383 412

  
384
  public void makeNextMove(int size, int num)
413
  public String retPrevMove(int size, int cat, int pat)
385 414
    {
386
    if( size>=0 && size<NUM_CUBES && num>=0 && num< numCategories[size] )
415
    if( size>=0 && size<NUM_CUBES && cat>=0 && cat< numCategories[size] )
387 416
      {
388
      Category c = mCategories[size].elementAt(num);
389
      if( c!=null ) c.makeNextMove();
417
      Category c = mCategories[size].elementAt(cat);
418
      if( c!=null ) return c.retPrevMove(pat);
390 419
      }
391
    }
392

  
393
///////////////////////////////////////////////////////////////////////////////////////////////////
394 420

  
395
  public void makePrevMove(int size, int num)
396
    {
397
    if( size>=0 && size<NUM_CUBES && num>=0 && num< numCategories[size] )
398
      {
399
      Category c = mCategories[size].elementAt(num);
400
      if( c!=null ) c.makePrevMove();
401
      }
421
    return "";
402 422
    }
403 423

  
404 424
///////////////////////////////////////////////////////////////////////////////////////////////////
405 425

  
406
  public void initializeCube(int size,int num)
426
  public String retInitializationString(int size,int cat, int pat)
407 427
    {
408
    if( size>=0 && size<NUM_CUBES && num>=0 && num< numCategories[size] )
428
    if( size>=0 && size<NUM_CUBES && cat>=0 && cat< numCategories[size] )
409 429
      {
410
      Category c = mCategories[size].elementAt(num);
411
      if( c!=null ) c.initializeCube();
430
      Category c = mCategories[size].elementAt(cat);
431
      if( c!=null ) return c.retInitializationString(pat);
412 432
      }
413
    }
414

  
415
///////////////////////////////////////////////////////////////////////////////////////////////////
416 433

  
417
  public int getNumCategories(int size)
418
    {
419
    return size>=0 && size<NUM_CUBES ? numCategories[size] : 0;
434
    return "";
420 435
    }
421 436
}
src/main/java/org/distorted/uistate/RubikStatePattern.java
39 39

  
40 40
public class RubikStatePattern extends RubikStateAbstract
41 41
  {
42
  private TextView mText;
42 43
  private Button mBackButton;
43 44
  private int mSize;
44 45

  
......
92 93
    // TOP ////////////////////////////
93 94
    LinearLayout layoutTop = act.findViewById(R.id.mainTitle);
94 95
    layoutTop.removeAllViews();
95
    TextView text = (TextView)inflater.inflate(R.layout.upper_text, null);
96
    text.setText(R.string.patterns);
97
    layoutTop.addView(text);
96
    mText = (TextView)inflater.inflate(R.layout.upper_text, null);
97
    mText.setText(R.string.patterns);
98
    layoutTop.addView(mText);
98 99

  
99 100
    // BOT ////////////////////////////
100 101
    DisplayMetrics metrics = act.getResources().getDisplayMetrics();
......
154 155
      });
155 156
    }
156 157

  
158
///////////////////////////////////////////////////////////////////////////////////////////////////
159

  
160
  public void setPatternName(String name)
161
    {
162
    mText.setText(name);
163
    }
164

  
157 165
///////////////////////////////////////////////////////////////////////////////////////////////////
158 166

  
159 167
  public void savePreferences(SharedPreferences.Editor editor)
src/main/res/layout/dialog_pattern_tab.xml
1 1
<?xml version="1.0" encoding="utf-8"?>
2
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:id="@+id/tabScrollView"
4
    android:background="@color/grey"
5
    android:paddingBottom="10dp"
6
    android:layout_width="match_parent"
7
    android:layout_height="match_parent">
8

  
9
    <LinearLayout
10
        android:id="@+id/tabLayout"
2
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
11 3
        android:layout_width="match_parent"
12 4
        android:layout_height="wrap_content"
13 5
        android:orientation="vertical" >
14
    </LinearLayout>
15 6

  
16
</ScrollView>
7
    <Spinner
8
        android:id="@+id/pattern_category_spinner"
9
        android:layout_marginLeft="20dp"
10
        android:layout_marginRight="20dp"
11
        android:layout_marginTop="10dp"
12
        android:layout_marginBottom="10dp"
13
        android:layout_width="match_parent"
14
        android:layout_height="50dp"/>
15

  
16
    <ScrollView
17
        android:id="@+id/tabScrollView"
18
        android:paddingBottom="10dp"
19
        android:background="@color/grey"
20
        android:layout_width="match_parent"
21
        android:layout_height="match_parent">
22

  
23
        <LinearLayout
24
            android:id="@+id/tabLayout"
25
            android:layout_width="match_parent"
26
            android:layout_height="wrap_content"
27
            android:orientation="vertical">
28
        </LinearLayout>
29
    </ScrollView>
17 30

  
31
</LinearLayout>

Also available in: Unified diff