1/*
2 * Copyright (C) 2014 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
17package com.android.tv.settings.device.storage;
18
19import com.android.tv.settings.R;
20import com.android.tv.settings.device.storage.StorageMeasurement.MeasurementDetails;
21import com.android.tv.settings.device.storage.StorageMeasurement.MeasurementReceiver;
22import com.android.tv.settings.dialog.old.Action;
23import com.android.tv.settings.dialog.old.ActionAdapter;
24import com.android.tv.settings.dialog.old.ActionFragment;
25import com.android.tv.settings.dialog.old.ContentFragment;
26import com.android.tv.settings.dialog.old.DialogActivity;
27
28import android.app.Activity;
29import android.content.res.TypedArray;
30import android.graphics.Color;
31import android.os.Bundle;
32import android.os.Environment;
33import android.os.Handler;
34import android.os.Message;
35import android.text.format.Formatter;
36import android.widget.ImageView;
37import android.widget.TextView;
38
39import java.util.ArrayList;
40import java.util.Collections;
41import java.util.HashMap;
42import java.util.List;
43
44/**
45 * Activity that shows storage information.
46 */
47public class StorageActivity extends DialogActivity {
48
49    private static final long INVALID_SIZE = -1;
50
51    private static final int UPDATE_MSG = 1;
52
53    private ActionFragment mActionFragment;
54    private StorageContentFragment mContentFragment;
55    private StorageMeasurement mMeasure;
56    private boolean mResumed;
57
58    private final MeasurementReceiver mReceiver = new MeasurementReceiver() {
59
60        private long[] mLastApproximateDetails = null;
61        private MeasurementDetails mLastMeasurementDetails = null;
62
63        private final Handler mUpdateHandler = new Handler() {
64            @Override
65            public void handleMessage(Message msg) {
66                if (!mResumed) {
67                    return;
68                }
69                switch (msg.what) {
70                    case UPDATE_MSG:
71                        if (mLastMeasurementDetails != null) {
72                            StorageActivity.this.updateDetails(mLastMeasurementDetails);
73                        } else if (mLastApproximateDetails != null) {
74                            StorageActivity.this.updateApproximate(
75                                    mLastApproximateDetails[0], mLastApproximateDetails[1]);
76                        }
77                        break;
78                    default:
79                        break;
80                }
81            }
82        };
83
84        private void startUiUpdateTimer() {
85            mUpdateHandler.sendEmptyMessageDelayed(UPDATE_MSG, 1000);
86        }
87
88        @Override
89        public void updateApproximate(StorageMeasurement meas, long totalSize, long availSize) {
90            mLastApproximateDetails = new long[] { totalSize, availSize };
91            startUiUpdateTimer();
92        }
93
94        @Override
95        public void updateDetails(StorageMeasurement meas, MeasurementDetails details) {
96            mLastMeasurementDetails = details;
97            startUiUpdateTimer();
98        }
99    };
100
101    @Override
102    protected void onCreate(Bundle savedInstanceState) {
103        super.onCreate(savedInstanceState);
104        mActionFragment = ActionFragment.newInstance(getActions());
105        mContentFragment = StorageContentFragment.newInstance(getString(R.string.storage_title),
106                getString(R.string.header_category_device),
107                String.format(getString(R.string.storage_size), formatSize(INVALID_SIZE)),
108                R.drawable.ic_settings_storage, getResources().getColor(R.color.icon_background));
109        setContentAndActionFragments(mContentFragment, mActionFragment);
110        mMeasure = StorageMeasurement.getInstance(this, null);
111    }
112
113    @Override
114    protected void onResume() {
115        super.onResume();
116        mResumed = true;
117        mMeasure.setReceiver(mReceiver);
118        mMeasure.invalidate();
119        mMeasure.measure();
120    }
121
122    @Override
123    protected void onPause() {
124        mMeasure.cleanUp();
125        mResumed = false;
126        super.onPause();
127    }
128
129    private ArrayList<Action> getActions() {
130        return getActions(INVALID_SIZE);
131    }
132
133    private ArrayList<Action> getActions(long availSize) {
134        return getActions(availSize, INVALID_SIZE, INVALID_SIZE, INVALID_SIZE, INVALID_SIZE,
135                INVALID_SIZE, INVALID_SIZE);
136    }
137
138    private ArrayList<Action> getActions(long availSize, long appsSize, long dcimSize,
139            long musicSize, long downloadsSize, long cacheSize, long miscSize) {
140        ArrayList<Action> actions = new ArrayList<Action>();
141        actions.add(StorageItem.APPS.toAction(getResources(), formatSize(appsSize)));
142        actions.add(StorageItem.PICTURES_VIDEO.toAction(getResources(), formatSize(dcimSize)));
143        actions.add(StorageItem.AUDIO.toAction(getResources(), formatSize(musicSize)));
144        actions.add(StorageItem.DOWNLOADS.toAction(getResources(), formatSize(downloadsSize)));
145        actions.add(StorageItem.CACHED_DATA.toAction(getResources(), formatSize(cacheSize)));
146        actions.add(StorageItem.MISC.toAction(getResources(), formatSize(miscSize)));
147        actions.add(StorageItem.AVAILABLE.toAction(getResources(), formatSize(availSize)));
148        return actions;
149    }
150
151    private void updateActions(ArrayList<Action> actions) {
152        ((ActionAdapter) mActionFragment.getAdapter()).setActions(actions);
153    }
154
155    private void updateApproximate(long totalSize, long availSize) {
156
157        final long usedSize = totalSize - availSize;
158
159        ArrayList<PercentageBarChart.Entry> entries = new ArrayList<PercentageBarChart.Entry>();
160        entries.add(new PercentageBarChart.Entry(
161                0, usedSize / (float) totalSize, android.graphics.Color.GRAY));
162
163        updateActions(getActions(availSize));
164        updateStorageContentFragment(totalSize, entries);
165    }
166
167    private void updateDetails(MeasurementDetails details) {
168
169        final long dcimSize = totalValues(details.mediaSize, Environment.DIRECTORY_DCIM,
170                Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_PICTURES);
171
172        final long musicSize = totalValues(details.mediaSize, Environment.DIRECTORY_MUSIC,
173                Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
174                Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS);
175
176        final long downloadsSize = totalValues(details.mediaSize, Environment.DIRECTORY_DOWNLOADS);
177
178        ArrayList<PercentageBarChart.Entry> entries = new ArrayList<PercentageBarChart.Entry>();
179
180        addEntry(entries, StorageItem.APPS, details.appsSize, details.totalSize);
181        addEntry(entries, StorageItem.PICTURES_VIDEO, dcimSize, details.totalSize);
182        addEntry(entries, StorageItem.AUDIO, musicSize, details.totalSize);
183        addEntry(entries, StorageItem.DOWNLOADS, downloadsSize, details.totalSize);
184        addEntry(entries, StorageItem.CACHED_DATA, details.cacheSize, details.totalSize);
185        addEntry(entries, StorageItem.MISC, details.miscSize, details.totalSize);
186
187        Collections.sort(entries);
188
189        updateActions(getActions(
190                details.availSize, details.appsSize, dcimSize, musicSize, downloadsSize,
191                details.cacheSize, details.miscSize));
192        updateStorageContentFragment(details.totalSize, entries);
193    }
194
195    private static long totalValues(HashMap<String, Long> map, String... keys) {
196        long total = 0;
197        for (String key : keys) {
198            total += map.get(key);
199        }
200        return total;
201    }
202
203    private String formatSize(long size) {
204        return (size == INVALID_SIZE) ? getString(R.string.storage_calculating_size)
205                : Formatter.formatShortFileSize(this, size);
206    }
207
208    private void addEntry(List<PercentageBarChart.Entry> entries, StorageItem storageItem,
209            long size, long totalSize) {
210        if (size > 0) {
211            entries.add(new PercentageBarChart.Entry(
212                    storageItem.ordinal(), size / (float) totalSize,
213                    storageItem.getColor(getResources())));
214        }
215    }
216
217    private void updateStorageContentFragment(long totalSize) {
218        updateStorageContentFragment(totalSize, new ArrayList<PercentageBarChart.Entry>());
219    }
220
221    private void updateStorageContentFragment(
222            long totalSize, ArrayList<PercentageBarChart.Entry> entries) {
223        mContentFragment.updateEntries(entries);
224        mContentFragment.setDescriptionText(
225                String.format(getString(R.string.storage_size), formatSize(totalSize)));
226    }
227}
228