/* * Copyright (c) 2013 Allogy Interactive. * * 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.allogy.app; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.ContentValues; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.EditText; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.allogy.app.adapter.CourseAdapter; import com.allogy.app.adapter.CourseAdapter.CourseHolder; import com.allogy.app.adapter.LessonAdapter; import com.allogy.app.adapter.LessonAdapter.LessonHolder; import com.allogy.app.adapter.PublisherAdapter; import com.allogy.app.adapter.PublisherAdapter.PublisherHolder; import com.allogy.app.provider.Academic; import com.allogy.app.provider.Academic.Courses; import com.allogy.app.provider.Academic.Lesson; import com.allogy.app.provider.Academic.LessonFiles; import com.allogy.app.ui.AutoButton; import com.allogy.encryption.DecryptFile; import com.allogy.encryption.SmsCrypto; public class ClassroomActivity extends BaseActivity { private static final String SAVED_STATE = "savedState"; private static final String SAVED_PUB = "pubState"; private static final String SAVED_COURSE = "courseState"; private static long mPubId = -1; private static long mCourseId = -1; private static long mLessonId = -1; private static int mState; private static final int STATE_PUBLISHER = 0; private static final int STATE_COURSE = 1; private static final int STATE_LESSON = 2; private static TextView mEmptyView; private static TextView mLoadingView; private static ListView mContentList; private static AutoButton mPublishersButton; private static AutoButton mCoursesButton; private static AutoButton mLessonsButton; private static final int MSG_SHOW_INPUT_CODE = 0; private static final int MSG_DISMISS_INPUT_CODE = 1; private static final int MSG_SHOW_UNLOCKING = 2; private static final int MSG_DISMISS_UNLOCKING = 3; private static final int MSG_SHOW_PROGRESS = 4; private static final int MSG_HIDE_PROGRESS = 5; private static ProgressBar mProgress; private static AlertDialog mEnterCode; private static ProgressDialog mUnlockProgress; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SHOW_INPUT_CODE: mEnterCode.show(); break; case MSG_DISMISS_INPUT_CODE: mEnterCode.dismiss(); break; case MSG_SHOW_UNLOCKING: mUnlockProgress.show(); break; case MSG_DISMISS_UNLOCKING: mUnlockProgress.dismiss(); case MSG_SHOW_PROGRESS: mProgress.setVisibility(View.VISIBLE); break; case MSG_HIDE_PROGRESS: mProgress.setVisibility(View.GONE); break; } } }; @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(SAVED_STATE, mState); if (mState == STATE_COURSE) { outState.putLong(SAVED_PUB, mPubId); } if (mState == STATE_LESSON) { outState.putLong(SAVED_PUB, mPubId); outState.putLong(SAVED_COURSE, mCourseId); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_classroom); mProgress = (ProgressBar) findViewById(R.id.action_bar_progress); mLoadingView = (TextView) findViewById(R.id.classroom_loading); mEmptyView = (TextView) LayoutInflater.from(this).inflate( R.layout.empty_view, null); mContentList = (ListView) findViewById(R.id.classroom_listview); ((ViewGroup) mContentList.getParent()).addView(mEmptyView); mContentList.setEmptyView(mEmptyView); mPublishersButton = (AutoButton) findViewById(R.id.classroom_button_publishers); mCoursesButton = (AutoButton) findViewById(R.id.classroom_button_courses); mLessonsButton = (AutoButton) findViewById(R.id.classroom_button_lessons); CreateDialogs(); SetClickers(); if (savedInstanceState != null) { mState = savedInstanceState.getInt(SAVED_STATE); if (savedInstanceState.containsKey(SAVED_PUB)) { mPubId = savedInstanceState.getLong(SAVED_PUB); } if (savedInstanceState.containsKey(SAVED_COURSE)) { mCourseId = savedInstanceState.getLong(SAVED_COURSE); } } else { mState = STATE_PUBLISHER; } PrepareDisplayState(); } private void CreateDialogs() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getResources().getString(R.string.prompt_unlock_code)); builder.setCancelable(false); builder.setView(LayoutInflater.from(this).inflate( R.layout.dialog_unlock, null)); builder.setPositiveButton(getResources().getString(R.string.enter), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { unlockCourse(); mHandler.sendEmptyMessage(MSG_DISMISS_INPUT_CODE); } }); builder.setNegativeButton(getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mCourseId = -1; mHandler.sendEmptyMessage(MSG_DISMISS_INPUT_CODE); } }); mEnterCode = builder.create(); ProgressDialog dialog = new ProgressDialog(this, ProgressDialog.STYLE_HORIZONTAL); dialog.setTitle("Unlocking Content"); dialog.setMessage("This could take up to 2 minutes"); mUnlockProgress = dialog; } private void SetClickers() { mPublishersButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mState = STATE_PUBLISHER; PrepareDisplayState(); } }); mCoursesButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mState = STATE_COURSE; PrepareDisplayState(); } }); mLessonsButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mState = STATE_LESSON; PrepareDisplayState(); } }); } private void PrepareDisplayState() { switch (mState) { case STATE_PUBLISHER: mLoadingView.setVisibility(View.GONE); mContentList.setAdapter(new PublisherAdapter(this)); mContentList.setOnItemClickListener(publisherClicker); mPublishersButton.setSelected(true); mCoursesButton.setSelected(false); mLessonsButton.setSelected(false); break; case STATE_COURSE: mLoadingView.setVisibility(View.GONE); mContentList.setAdapter(new CourseAdapter(this, mPubId)); mContentList.setOnItemClickListener(courseClicker); mCoursesButton.setSelected(true); mPublishersButton.setSelected(false); mLessonsButton.setSelected(false); break; case STATE_LESSON: mContentList.setVisibility(View.GONE); mLoadingView.setVisibility(View.GONE); mContentList.setAdapter(new LessonAdapter(this, mCourseId, mPubId)); mContentList.setOnItemClickListener(lessonClicker); mLessonsButton.setSelected(true); mPublishersButton.setSelected(false); mCoursesButton.setSelected(false); break; default: } // disable buttons if no publisher or course are chosen if (mPubId == -1) mCoursesButton.setEnabled(false); else mCoursesButton.setEnabled(true); if (mCourseId == -1) mLessonsButton.setEnabled(false); else mLessonsButton.setEnabled(true); Log.i("ClassroomActivity", mPubId + " " + mCourseId); } private void unlockCourse() { String code = ((EditText) mEnterCode.findViewById(R.id.dialog_unlock)) .getText().toString(); // take in the unlock code and check it try { SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(this); String telephone = prefs.getString(SettingsActivity.PREF_PHONE, null); telephone.replace("+", ""); long salt = Long.parseLong(telephone.trim()); Log.i("ClassroomActivity", "attempting sms decode: " + code + " " + salt); Log.i("ClassroomActivity", "course " + mCourseId); int result = SmsCrypto.decode(code, salt); Log.i("ClassroomActivity", "sms decode: " + result); if (result == (mCourseId + 8000)) { // mark course as unlocked ContentValues values = new ContentValues(); values.put(Courses.STATUS, Academic.STATUS_UNLOCKED); getContentResolver().update(Courses.CONTENT_URI, values, String.format("%s=?", Courses._ID), new String[] { Long.toString(mCourseId) }); mState = STATE_LESSON; PrepareDisplayState(); } else { mCourseId = -1; mHandler.sendEmptyMessage(MSG_DISMISS_INPUT_CODE); Toast.makeText(this, "Invalid Code", Toast.LENGTH_SHORT).show(); } } catch (Exception e) { mCourseId = -1; Log.i("ClassroomActivity", "General Exception " + e.getMessage()); Toast.makeText(ClassroomActivity.this, "Invalid Code", Toast.LENGTH_SHORT).show(); } } private void decryptLesson() { Cursor c = managedQuery( Academic.LessonFiles.CONTENT_URI, new String[] { LessonFiles._ID, LessonFiles.URI }, String.format("%s=? AND (%s=? OR %s=?)", LessonFiles.LESSON_ID, LessonFiles.MEDIA_TYPE, LessonFiles.MEDIA_TYPE), new String[] { Long.toString(mLessonId), Integer.toString(Academic.CONTENT_TYPE_AUDIO), Integer.toString(Academic.CONTENT_TYPE_VIDEO) }, LessonFiles.SORT_ORDER_DEFAULT); if (c != null) { c.moveToFirst(); String file = c.getString(c.getColumnIndex(LessonFiles.URI)); Log.i("ClassroomActivity", "decrypting content file:" + file); new ContentUnlocker().execute(file); } } /** * Originally the lessons were to be unlocked via purchase and SMS code This * was changed; In this context unlocking means the decryption of the media * file has been completed */ private void markLessonDecrypted() { // mark lesson as unlocked ContentValues lessonVals = new ContentValues(); lessonVals.put(Academic.Lesson.LOCKED, Academic.STATUS_UNLOCKED); getContentResolver().update(Academic.Lesson.CONTENT_URI, lessonVals, String.format("%s=?", Lesson._ID), new String[] { Long.toString(mLessonId) }); // create the deadline GenerateDeadline(); Intent i = new Intent(); i.setClass(ClassroomActivity.this, LessonActivity.class); i.putExtra(LessonActivity.INTENT_EXTRA_PUBLISHER_ID, mPubId); i.putExtra(LessonActivity.INTENT_EXTRA_COURSE_ID, mCourseId); i.putExtra(LessonActivity.INTENT_EXTRA_LESSON_ID, mLessonId); startActivity(i); } /** * Creates the deadlines */ private void GenerateDeadline() { if (mLessonId != -1) { // show progress indicators mLoadingView.setVisibility(View.VISIBLE); mContentList.setVisibility(View.GONE); mHandler.sendEmptyMessage(MSG_SHOW_PROGRESS); Cursor c = managedQuery(Academic.Lesson.CONTENT_URI, new String[] { Academic.Lesson._ID, Academic.Lesson.DEADLINE_RAW, Academic.Lesson.DEADLINE_TYPE }, String.format("%s=?", Academic.Lesson._ID), new String[] { Long.toString(mLessonId) }, Academic.Lesson.SORT_ORDER_DEFAULT); c.moveToFirst(); int mIdIndex = c.getColumnIndexOrThrow(Academic.Lesson._ID); int mDeadlineRawIndex = c .getColumnIndexOrThrow(Academic.Lesson.DEADLINE_RAW); int mDeadlineTypeIndex = c .getColumnIndexOrThrow(Academic.Lesson.DEADLINE_TYPE); try { // lesson variables long id = c.getLong(mIdIndex); Long deadline = c.getLong(mDeadlineRawIndex); // #897 : If the deadline entered is 0, then hide the progress and return // There is nothing to be updated if(deadline.intValue() == 0) { mHandler.sendEmptyMessage(MSG_HIDE_PROGRESS); return; } int type = c.getInt(mDeadlineTypeIndex); boolean doInsert = false; long deadlineValue = 0; switch (type) { case Academic.Lesson.DEADLINE_TYPE_NA: break; case Academic.Lesson.DEADLINE_TYPE_ABSOLUTE: // unix value is seconds since epoc, we need millis deadlineValue = deadline * 1000; doInsert = true; break; case Academic.Lesson.DEADLINE_TYPE_RELATIVE: // deadline = current time + a specified amount of time deadlineValue = System.currentTimeMillis() + deadline*1000; doInsert = true; break; } if (doInsert) { // insert deadline into Deadlines table ContentValues vals = new ContentValues(); vals.put(Academic.Deadline.CONTENT_ID, id); vals.put(Academic.Deadline.TIME, deadlineValue); vals.put(Academic.Deadline.CONTENT_TYPE, Academic.CONTENT_TYPE_LESSON); Uri deadlineURI = getContentResolver().insert( Academic.Deadline.CONTENT_URI, vals); Long deadlineID = Long.parseLong(deadlineURI .getLastPathSegment()); // insert the deadline_id back into the Lesson table ContentValues updateVals = new ContentValues(); vals.put(Academic.Lesson.DEADLINE_ID, deadlineID); getContentResolver().update(Academic.Lesson.CONTENT_URI, updateVals, String.format("%s=?", Academic.Lesson._ID), new String[] { Long.toString(id) }); } } catch (Exception e) { e.printStackTrace(); Log.i("ClassroomActivity", e.getMessage()); } // dismiss progress indicator mHandler.sendEmptyMessage(MSG_HIDE_PROGRESS); } } final OnItemClickListener publisherClicker = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { PublisherHolder holder = (PublisherHolder) view.getTag(); mPubId = holder.id; mState = STATE_COURSE; PrepareDisplayState(); } }; final OnItemClickListener courseClicker = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { CourseHolder holder = (CourseHolder) view.getTag(); mCourseId = holder.id; if (holder.status == Academic.STATUS_LOCKED) mHandler.sendEmptyMessage(MSG_SHOW_INPUT_CODE); else { mState = STATE_LESSON; PrepareDisplayState(); } } }; final OnItemClickListener lessonClicker = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { LessonHolder holder = (LessonHolder) view.getTag(); mLessonId = holder.id; if (holder.locked) { // Check if the encryption is enabled, if yes then decrypt the file // or else just mark the lesson to be unlocked if(((AllogyApplication) getApplicationContext()).isEncryptionEnabled()) { decryptLesson(); } else { markLessonDecrypted(); } } else { Intent i = new Intent(); i.setClass(ClassroomActivity.this, LessonActivity.class); i.putExtra(LessonActivity.INTENT_EXTRA_PUBLISHER_ID, mPubId); i.putExtra(LessonActivity.INTENT_EXTRA_COURSE_ID, mCourseId); i.putExtra(LessonActivity.INTENT_EXTRA_LESSON_ID, holder.id); startActivity(i); } } }; private class ContentUnlocker extends AsyncTask<String, String, String> { @Override protected void onPostExecute(String result) { System.gc(); mHandler.sendEmptyMessage(MSG_DISMISS_UNLOCKING); markLessonDecrypted(); } @Override protected void onPreExecute() { mHandler.sendEmptyMessage(MSG_SHOW_UNLOCKING); } @Override protected String doInBackground(String... params) { String file = params[0]; if (file != null && !file.equals("")) { try { DecryptFile.decryptFile(file); } catch (Exception e) { Log.i("ContentUnlocker", e.getMessage()); } } return null; } } }